Commit: patch 9.2.0888: mapping: modifier is not recognized after a partial mapping

2 views
Skip to first unread message

Christian Brabandt

unread,
3:15 PM (7 hours ago) 3:15 PM
to vim...@googlegroups.com
patch 9.2.0888: mapping: modifier is not recognized after a partial mapping

Commit: https://github.com/vim/vim/commit/62c8cdc37c2a1b3ef825022cfa343d5c16019831
Author: Hirohito Higashi <h.eas...@gmail.com>
Date: Fri Jul 31 19:07:27 2026 +0000

patch 9.2.0888: mapping: modifier is not recognized after a partial mapping

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) <nor...@anthropic.com>
Signed-off-by: Hirohito Higashi <h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 6329ac24a..38b6ae454 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -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
diff --git a/src/getchar.c b/src/getchar.c
index 83e4be3eb..9eef2235f 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -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
diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim
index adabeeedb..a5e80d72d 100644
--- a/src/testdir/test_termcodes.vim
+++ b/src/testdir/test_termcodes.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index b3bd5174f..0fdd61f59 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 888,
/**/
887,
/**/
Reply all
Reply to author
Forward
0 new messages