patch 9.2.0192: not correctly recognizing raw key codes

Commit: 
https://github.com/vim/vim/commit/c4d212257d61f5c2a9cd919486288c747aaaa05d
Author: AstroSnail <[email protected]>
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 <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

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,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1w2bzJ-00AWBe-Bb%40256bit.org.

Raspunde prin e-mail lui