]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0888: mapping: modifier is not recognized after a partial mapping v9.2.0888
authorHirohito Higashi <h.east.727@gmail.com>
Fri, 31 Jul 2026 19:07:27 +0000 (19:07 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 31 Jul 2026 19:08:35 +0000 (19:08 +0000)
Problem:  With the key protocol enabled, a mapping for a key with a modifier
          is not used when it follows a partial match of another mapping.
Solution: When the modifier was merged into the key in the typeahead, mark
          the key, so that the mapping for the simplified key can be used.

fixes:  #12002
closes: #20898

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/todo.txt
src/getchar.c
src/testdir/test_termcodes.vim
src/version.c

index 6329ac24a6f6ac977a5f1b97c0dccffe439fdf67..38b6ae45455a9600e76928fdb722eb27bea2e274 100644 (file)
@@ -1,4 +1,4 @@
-*todo.txt*     For Vim version 9.2.  Last change: 2026 Jul 30
+*todo.txt*     For Vim version 9.2.  Last change: 2026 Jul 31
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -36,10 +36,6 @@ browser use: https://github.com/vim/vim/issues/1234
                                                        *known-bugs*
 -------------------- Known bugs and current work -----------------------
 
-Mapping with modifier is not recognized after a partial mapping.  Probably
-because the typeahead was simplified when looking for a matching mapping.
-Need to somehow undo the simplification.  #12002
-
 With 'smoothscroll' the scroll position is lost when a window is temporarily
 squeezed to a couple of lines, for example ":help" followed by ":close".  In
 restore_snapshot_rec() restore more values from the snapshot, instead of
index 83e4be3ebb27c0d1c179b9ccce434e8e71d85e2a..9eef2235f87950a408b4966ade58ae1f2e6ef9a6 100644 (file)
@@ -78,6 +78,7 @@ static int    KeyNoremap = 0;     // remapping flags
 #define RM_NONE                1       // tb_noremap: don't remap
 #define RM_SCRIPT      2       // tb_noremap: remap local script mappings
 #define RM_ABBR                4       // tb_noremap: don't remap, do abbrev.
+#define RM_SIMPLIFIED  8       // tb_noremap: modifiers merged into the key
 
 // typebuf.tb_buf has three parts: room in front (for result of mappings), the
 // middle for typeahead and room for new characters (which needs to be 3 *
@@ -2770,6 +2771,7 @@ check_simplify_modifier(int max_offset)
            {
                char_u  new_string[MB_MAXBYTES];
                int     len;
+               int     key_offset = offset;
 
                if (offset == 0)
                {
@@ -2797,10 +2799,17 @@ check_simplify_modifier(int max_offset)
                else
                {
                    tp[2] = modifier;
-                   if (put_string_in_typebuf(offset + 3, 1, new_string, len,
+                   key_offset = offset + 3;
+                   if (put_string_in_typebuf(key_offset, 1, new_string, len,
                                                        NULL, 0, NULL) == FAIL)
                        return -1;
                }
+
+               // A mapping for the simplified key can be used for it even
+               // when the key protocol is enabled.
+               for (int i = 0; i < len; ++i)
+                   typebuf.tb_noremap[typebuf.tb_off + key_offset + i]
+                                                           |= RM_SIMPLIFIED;
                return len;
            }
        }
@@ -2939,7 +2948,9 @@ handle_mapping(
            if (mp->m_keys[0] == tb_c1
                    && (mp->m_mode & local_State)
                    && !(mp->m_simplified && key_protocol_enabled()
-                                                    && typebuf.tb_maplen == 0)
+                           && typebuf.tb_maplen == 0
+                           && (typebuf.tb_noremap[typebuf.tb_off]
+                                                       & RM_SIMPLIFIED) == 0)
                    && ((mp->m_mode & MODE_LANGMAP) == 0
                                                    || typebuf.tb_maplen == 0))
            {
@@ -3002,7 +3013,7 @@ handle_mapping(
                    // If only script-local mappings are allowed, check if the
                    // mapping starts with K_SNR.
                    s = typebuf.tb_noremap + typebuf.tb_off;
-                   if (*s == RM_SCRIPT
+                   if ((*s & ~RM_SIMPLIFIED) == RM_SCRIPT
                            && (mp->m_keys[0] != K_SPECIAL
                                || mp->m_keys[1] != KS_EXTRA
                                || mp->m_keys[2] != KE_SNR))
@@ -3126,8 +3137,8 @@ handle_mapping(
        if (in_osc || no_mapping == 0 || allow_keys != 0)
        {
            if (in_osc || ((typebuf.tb_maplen == 0
-                           || (p_remap && typebuf.tb_noremap[
-                                                   typebuf.tb_off] == RM_YES))
+                           || (p_remap && (typebuf.tb_noremap[typebuf.tb_off]
+                                               & ~RM_SIMPLIFIED) == RM_YES))
                    && !*timedout))
                keylen = check_termcode(max_mlen + 1, NULL, 0, NULL);
            else
@@ -3635,7 +3646,8 @@ vgetorpeek(int advance)
                                gotchars(typebuf.tb_buf
                                                 + typebuf.tb_off, 1);
                            }
-                           KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
+                           KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]
+                                                           & ~RM_SIMPLIFIED;
                            del_typebuf(1, 0);
                        }
                        break;  // got character, break the for loop
index adabeeedbf2126d0dbcf266d0558b78481e9e0ff..a5e80d72d1a24500105f09a4fa4dcb21fa1770f8 100644 (file)
@@ -2298,6 +2298,30 @@ func Test_modifyOtherKeys_mapped()
   set timeoutlen&
 endfunc
 
+func Test_modifyOtherKeys_after_partial_mapping()
+  new
+  set timeoutlen=10
+  imap <C-J> x
+  imap ab y
+  call setline(1, '')
+
+  " "a" is a partial match for the "ab" mapping, CTRL-J is simplified while
+  " looking for that mapping and must still match the <C-J> mapping.
+  call feedkeys("aa" .. GetEscCodeCSI27('J', 5) .. "\<Esc>", 'Lx!')
+  call assert_equal('ax', getline(1))
+
+  " A NL that was not simplified from the key protocol does not use the
+  " mapping for CTRL-J.
+  %d _
+  call feedkeys("aa\<C-J>\<Esc>", 'Lx!')
+  call assert_equal(['a', ''], getline(1, '$'))
+
+  iunmap <C-J>
+  iunmap ab
+  set timeoutlen&
+  bwipe!
+endfunc
+
 func Test_modifyOtherKeys_ambiguous_mapping()
   new
   set timeoutlen=10
index b3bd5174f6c23eb7bedc98ddb3708a540998a205..0fdd61f59f5222befa590b278597a6ee640de5ff 100644 (file)
@@ -758,6 +758,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    888,
 /**/
     887,
 /**/