# HG changeset patch # User Bram Moolenaar # Date 1651528804 -7200 # Node ID 53c608c7ea9ec30df130103d7b8ab4f1941b4fa8 # Parent 7782b07d29ca1a8a0528231bb6b4fe5f0ca32d82 patch 8.2.4858: K_SPECIAL may be escaped twice Commit: https://github.com/vim/vim/commit/db08887f24d20be11d184ce321bc0890613e42bd Author: zeertzjq Date: Mon May 2 22:53:45 2022 +0100 patch 8.2.4858: K_SPECIAL may be escaped twice Problem: K_SPECIAL may be escaped twice. Solution: Avoid double escaping. (closes https://github.com/vim/vim/issues/10340) diff --git a/src/highlight.c b/src/highlight.c --- a/src/highlight.c +++ b/src/highlight.c @@ -1356,7 +1356,7 @@ highlight_set_startstop_termcode(int idx // Copy characters from arg[] to buf[], translating <> codes. for (p = arg, off = 0; off < 100 - 6 && *p; ) { - len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL); + len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL); if (len > 0) // recognized special char off += len; else // copy as normal char diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -1265,6 +1265,7 @@ trans_special( char_u **srcp, char_u *dst, int flags, // FSK_ values + int escape_ks, // escape K_SPECIAL bytes in the character int *did_simplify) // FSK_SIMPLIFY and found or { int modifiers = 0; @@ -1274,18 +1275,18 @@ trans_special( if (key == 0) return 0; - return special_to_buf(key, modifiers, flags & FSK_KEYCODE, dst); + return special_to_buf(key, modifiers, escape_ks, dst); } /* * Put the character sequence for "key" with "modifiers" into "dst" and return * the resulting length. - * When "keycode" is TRUE prefer key code, e.g. K_DEL instead of DEL. + * When "escape_ks" is TRUE escape K_SPECIAL bytes in the character. * The sequence is not NUL terminated. * This is how characters in a string are encoded. */ int -special_to_buf(int key, int modifiers, int keycode, char_u *dst) +special_to_buf(int key, int modifiers, int escape_ks, char_u *dst) { int dlen = 0; @@ -1303,10 +1304,10 @@ special_to_buf(int key, int modifiers, i dst[dlen++] = KEY2TERMCAP0(key); dst[dlen++] = KEY2TERMCAP1(key); } - else if (has_mbyte && !keycode) + else if (escape_ks) + dlen = (int)(add_char2buf(key, dst + dlen) - dst); + else if (has_mbyte) dlen += (*mb_char2bytes)(key, dst + dlen); - else if (keycode) - dlen = (int)(add_char2buf(key, dst + dlen) - dst); else dst[dlen++] = key; diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -24,8 +24,8 @@ int vim_isspace(int x); 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 flags, int *did_simplify); -int special_to_buf(int key, int modifiers, int keycode, char_u *dst); +int trans_special(char_u **srcp, char_u *dst, int flags, int escape_ks, int *did_simplify); +int special_to_buf(int key, int modifiers, int escape_ks, char_u *dst); int find_special_key(char_u **srcp, int *modp, int flags, int *did_simplify); int may_adjust_key_for_ctrl(int modifiers, int key); int may_remove_shift_modifier(int modifiers, int key); diff --git a/src/term.c b/src/term.c --- a/src/term.c +++ b/src/term.c @@ -6104,7 +6104,7 @@ replace_termcodes( #endif slen = trans_special(&src, result + dlen, FSK_KEYCODE | ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY), - did_simplify); + TRUE, did_simplify); if (slen) { dlen += slen; diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -595,4 +595,26 @@ func Test_deep_recursion() call assert_fails("exe 'if ' .. repeat('(', 1002)", 'E1169: Expression too recursive: ((') endfunc +" K_SPECIAL in the modified character used be escaped, which causes +" double-escaping with feedkeys() or as the return value of an mapping, +" and doesn't match what getchar() returns, +func Test_modified_char_no_escape_special() + nnoremap let g:got_m_ellipsis += 1 + call feedkeys("\", 't') + call assert_equal("\", getchar()) + let g:got_m_ellipsis = 0 + call feedkeys("\", 'xt') + call assert_equal(1, g:got_m_ellipsis) + func Func() + return "\" + endfunc + nmap Func() + call feedkeys("\", 'xt') + call assert_equal(2, g:got_m_ellipsis) + delfunc Func + nunmap + unlet g:got_m_ellipsis + nunmap +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_feedkeys.vim b/src/testdir/test_feedkeys.vim --- a/src/testdir/test_feedkeys.vim +++ b/src/testdir/test_feedkeys.vim @@ -23,4 +23,15 @@ func Test_feedkeys_with_abbreviation() iunabbrev trigger endfunc +func Test_feedkeys_escape_special() + nnoremap … let g:got_ellipsis += 1 + call feedkeys('…', 't') + call assert_equal('…', getcharstr()) + let g:got_ellipsis = 0 + call feedkeys('…', 'xt') + call assert_equal(1, g:got_ellipsis) + unlet g:got_ellipsis + nunmap … +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -2721,8 +2721,8 @@ func Test_nr2char() call assert_equal('a', nr2char(97, 1)) call assert_equal('a', nr2char(97, 0)) - call assert_equal("\x80\xfc\b\xf4\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\"')) - call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\"')) + call assert_equal("\x80\xfc\b" .. nr2char(0x100000), eval('"\"')) + call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\"')) endfunc " Test for screenattr(), screenchar() and screenchars() functions diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -1643,4 +1643,19 @@ func Test_unmap_simplifiable() unmap endfunc +func Test_expr_map_escape_special() + nnoremap … let g:got_ellipsis += 1 + func Func() + return '…' + endfunc + nmap Func() + let g:got_ellipsis = 0 + call feedkeys("\", 'xt') + call assert_equal(1, g:got_ellipsis) + delfunc Func + nunmap + unlet g:got_ellipsis + nunmap … +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/typval.c b/src/typval.c --- a/src/typval.c +++ b/src/typval.c @@ -2069,11 +2069,10 @@ eval_string(char_u **arg, typval_T *rett { ++p; // A "\" form occupies at least 4 characters, and produces up - // to 21 characters (3 * 6 for the char and 3 for a modifier): - // reserve space for 18 extra. - // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x. + // to 9 characters (6 for the char and 3 for a modifier): + // reserve space for 5 extra. if (*p == '<') - extra += 18; + extra += 5; } } @@ -2168,7 +2167,7 @@ eval_string(char_u **arg, typval_T *rett if (p[1] != '*') flags |= FSK_SIMPLIFY; - extra = trans_special(&p, end, flags, NULL); + extra = trans_special(&p, end, flags, FALSE, NULL); if (extra != 0) { end += extra; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4858, +/**/ 4857, /**/ 4856,