changeset 28668:53c608c7ea9e v8.2.4858

patch 8.2.4858: K_SPECIAL may be escaped twice Commit: https://github.com/vim/vim/commit/db08887f24d20be11d184ce321bc0890613e42bd Author: zeertzjq <zeertzjq@outlook.com> 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)
author Bram Moolenaar <Bram@vim.org>
date Tue, 03 May 2022 00:00:04 +0200
parents 7782b07d29ca
children c832e8f9a2e1
files src/highlight.c src/misc2.c src/proto/misc2.pro src/term.c src/testdir/test_eval_stuff.vim src/testdir/test_feedkeys.vim src/testdir/test_functions.vim src/testdir/test_mapping.vim src/typval.c src/version.c
diffstat 10 files changed, 67 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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 <C-H> or <A-x>
 {
     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;
 
--- 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);
--- 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;
--- 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 <expr> mapping,
+" and doesn't match what getchar() returns,
+func Test_modified_char_no_escape_special()
+  nnoremap <M-…> <Cmd>let g:got_m_ellipsis += 1<CR>
+  call feedkeys("\<M-…>", 't')
+  call assert_equal("\<M-…>", getchar())
+  let g:got_m_ellipsis = 0
+  call feedkeys("\<M-…>", 'xt')
+  call assert_equal(1, g:got_m_ellipsis)
+  func Func()
+    return "\<M-…>"
+  endfunc
+  nmap <expr> <F2> Func()
+  call feedkeys("\<F2>", 'xt')
+  call assert_equal(2, g:got_m_ellipsis)
+  delfunc Func
+  nunmap <F2>
+  unlet g:got_m_ellipsis
+  nunmap <M-…>
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- 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 … <Cmd>let g:got_ellipsis += 1<CR>
+  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
--- 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('"\<M-' .. nr2char(0x100000) .. '>"'))
-  call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
+  call assert_equal("\x80\xfc\b" .. nr2char(0x100000), eval('"\<M-' .. nr2char(0x100000) .. '>"'))
+  call assert_equal("\x80\xfc\b" .. nr2char(0x40000000), eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
 endfunc
 
 " Test for screenattr(), screenchar() and screenchars() functions
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -1643,4 +1643,19 @@ func Test_unmap_simplifiable()
   unmap <C-I>
 endfunc
 
+func Test_expr_map_escape_special()
+  nnoremap … <Cmd>let g:got_ellipsis += 1<CR>
+  func Func()
+    return '…'
+  endfunc
+  nmap <expr> <F2> Func()
+  let g:got_ellipsis = 0
+  call feedkeys("\<F2>", 'xt')
+  call assert_equal(1, g:got_ellipsis)
+  delfunc Func
+  nunmap <F2>
+  unlet g:got_ellipsis
+  nunmap …
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/typval.c
+++ b/src/typval.c
@@ -2069,11 +2069,10 @@ eval_string(char_u **arg, typval_T *rett
 	{
 	    ++p;
 	    // A "\<x>" 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;
--- 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,