Commit: patch 9.2.0192: not correctly recognizing raw key codes

2 views
Skip to first unread message

Christian Brabandt

unread,
Mar 17, 2026, 5:32:06 PM (6 hours ago) Mar 17
to vim...@googlegroups.com
patch 9.2.0192: not correctly recognizing raw key codes

Commit: https://github.com/vim/vim/commit/c4d212257d61f5c2a9cd919486288c747aaaa05d
Author: AstroSnail <astro...@protonmail.com>
Date: Tue Mar 17 21:24:43 2026 +0000

patch 9.2.0192: not correctly recognizing raw key codes

Problem: When "k" is excluded from cpoptions, vim should be able to
recognize raw key codes in mappings and replace them with
builtin codes (e.g. ^[OA is replaced with <Up>) so that
changing the builtin code also changes the mapping to match.
Currently, this only works properly if the builtin code does
not contain modifiers (e.g. @;*).
Solution: Teach find_term_bykeys how to recognize keys with modifiers
(AstroSnail).

fixes: #19182
closes: #19643

Signed-off-by: AstroSnail <astro...@protonmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/term.c b/src/term.c
index 43cc9988c..e9cb91cdc 100644
--- a/src/term.c
+++ b/src/term.c
@@ -62,7 +62,7 @@ static void got_code_from_term(char_u *code, int len);
static void check_for_codes_from_term(void);
#endif
static void del_termcode_idx(int idx);
-static int find_term_bykeys(char_u *src);
+static int find_term_bykeys(char_u *src, int *len);
static int term_is_builtin(char_u *name);
static int term_7to8bit(char_u *p);
static void accept_modifiers_for_function_keys(void);
@@ -7094,13 +7094,14 @@ replace_termcodes(
*/
if (do_key_code)
{
- i = find_term_bykeys(src);
+ int len;
+ i = find_term_bykeys(src, &len);
if (i >= 0)
{
result[dlen++] = K_SPECIAL;
result[dlen++] = termcodes[i].name[0];
result[dlen++] = termcodes[i].name[1];
- src += termcodes[i].len;
+ src += len;
// If terminal code matched, continue after it.
continue;
}
@@ -7208,18 +7209,88 @@ replace_termcodes(
* Return the index in termcodes[], or -1 if not found.
*/
static int
-find_term_bykeys(char_u *src)
+find_term_bykeys(char_u *src, int *matchlen)
{
- int i;
- int slen = (int)STRLEN(src);
-
+ int i, j;
+ int len = (int)STRLEN(src);
+ int found = -1;
+ // Don't return a match for a single character
+ int foundlen = 1;
+ int slen, modslen;
+ int thislen;
+
+ // find longest match
+ // borrows part of check_termcode
for (i = 0; i < tc_len; ++i)
{
- if (slen == termcodes[i].len
+ slen = termcodes[i].len;
+ modslen = termcodes[i].modlen;
+
+ /*
+ * Check for code with modifier, like xterm uses:
+ * <Esc>[123;*X (modslen == slen - 3)
+ * <Esc>[@;*X (matches <Esc>[X and <Esc>[1;9X )
+ * Also <Esc>O*X and <M-O>*X (modslen == slen - 2).
+ * When there is a modifier the * matches a number.
+ * When there is no modifier the ;* or * is omitted.
+ */
+ if (modslen > 0)
+ {
+ if (len > modslen
+ && STRNCMP(termcodes[i].code, src, (size_t)modslen) == 0)
+ {
+ thislen = 0;
+
+ if (src[modslen] == termcodes[i].code[slen - 1])
+ // no modifiers
+ thislen = modslen + 1;
+ else if (src[modslen] != ';' && modslen == slen - 3)
+ // no match for "code;*X" with "code;"
+ continue;
+ else if (termcodes[i].code[modslen] == '@'
+ && (src[modslen] != '1'
+ || src[modslen + 1] != ';'))
+ // no match for "<Esc>[@" with "<Esc>[1;"
+ continue;
+ else
+ {
+ // Skip over the digits, the final char must
+ // follow. URXVT can use a negative value, thus
+ // also accept '-'.
+ for (j = slen - 2; j < len && (SAFE_isdigit(src[j])
+ || src[j] == '-' || src[j] == ';'); ++j)
+ ;
+ ++j;
+ if (len < j) // got a partial sequence
+ continue;
+ if (src[j - 1] != termcodes[i].code[slen - 1])
+ continue; // no match
+
+ thislen = j;
+ }
+
+ if (thislen > foundlen)
+ {
+ found = i;
+ foundlen = thislen;
+ }
+ }
+ }
+ else
+ {
+ if (slen > foundlen && len >= slen
&& STRNCMP(termcodes[i].code, src, (size_t)slen) == 0)
- return i;
+ {
+ found = i;
+ foundlen = slen;
+ }
+ }
}
- return -1;
+
+ if (matchlen != NULL && found >= 0)
+ *matchlen = foundlen;
+
+ return found;
}

/*
@@ -7525,7 +7596,7 @@ got_code_from_term(char_u *code, int len)
# endif
else
{
- i = find_term_bykeys(str);
+ i = find_term_bykeys(str, NULL);
if (i >= 0 && name[0] == termcodes[i].name[0]
&& name[1] == termcodes[i].name[1])
{
diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim
index 90cbc87df..c40e41a6e 100644
--- a/src/testdir/test_termcodes.vim
+++ b/src/testdir/test_termcodes.vim
@@ -2795,6 +2795,26 @@ func Test_home_is_not_khome()
let &t_kh = save_kh
endfunc

+func Test_raw_codes_in_mappings()
+ let save_cpo = &cpo
+ let save_ku = exists('&t_ku') ? &t_ku : ''
+
+ set cpo-=k
+ let &t_ku = "\<Esc>O*A"
+ exe "map X ^\<Esc>OAjk"
+ let &t_ku = ""
+
+ new
+ exe "normal iabc\<CR>abc\<CR>abc\<CR>abc\<Esc>XX"
+ call assert_equal(['abc', 'abc', 'abc', 'abc'], getline(1, '$'))
+ call assert_equal([0, 2, 1, 0], getpos('.'))
+
+ bwipe!
+ let &cpo = save_cpo
+ let &t_ku = save_ku
+ unmap X
+endfunc
+
func Test_terminal_builtin_without_gui()
CheckNotMSWindows

diff --git a/src/version.c b/src/version.c
index b2257997c..c3bae943a 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =

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