changeset 32369:ffbae151e462 v9.0.1516

patch 9.0.1516: cannot use special keys in <Cmd> mapping Commit: https://github.com/vim/vim/commit/3ab3a864814f903da8a158c01820e4fbe1013c08 Author: zeertzjq <zeertzjq@outlook.com> Date: Sat May 6 16:22:04 2023 +0100 patch 9.0.1516: cannot use special keys in <Cmd> mapping Problem: Cannot use special keys in <Cmd> mapping. Solution: Do allow for special keys in <Cmd> and <ScriptCmd> mappings. (closes #12326)
author Bram Moolenaar <Bram@vim.org>
date Sat, 06 May 2023 17:30:03 +0200
parents 2992e4adb0da
children f3abbfef6f19
files runtime/doc/map.txt src/errors.h src/getchar.c src/ops.c src/proto/getchar.pro src/testdir/test_mapping.vim src/version.c
diffstat 7 files changed, 61 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -408,10 +408,6 @@ Note:
 by <CR> in the {rhs} of the mapping definition.  |Command-line| mode is never
 entered.
 
-							*E1137*
-<Cmd> and <ScriptCmd> commands can have only normal characters and cannot
-contain special characters like function keys.
-
 
 1.3 MAPPING AND MODES					*:map-modes*
 			*mapmode-nvo* *mapmode-n* *mapmode-v* *mapmode-o*
--- a/src/errors.h
+++ b/src/errors.h
@@ -2894,8 +2894,7 @@ EXTERN char e_using_string_as_bool_str[]
 #endif
 EXTERN char e_cmd_mapping_must_end_with_cr_before_second_cmd[]
 	INIT(= N_("E1136: <Cmd> mapping must end with <CR> before second <Cmd>"));
-EXTERN char e_cmd_mapping_must_not_include_str_key[]
-	INIT(= N_("E1137: <Cmd> mapping must not include %s key"));
+// E1137 unused
 #ifdef FEAT_EVAL
 EXTERN char e_using_bool_as_number[]
 	INIT(= N_("E1138: Using a Bool as a Number"));
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -603,6 +603,26 @@ AppendToRedobuffLit(
 }
 
 /*
+ * Append "s" to the redo buffer, leaving 3-byte special key codes unmodified
+ * and escaping other K_SPECIAL and CSI bytes.
+ */
+    void
+AppendToRedobuffSpec(char_u *s)
+{
+    while (*s != NUL)
+    {
+	if (*s == K_SPECIAL && s[1] != NUL && s[2] != NUL)
+	{
+	    // insert special key literally
+	    add_buff(&redobuff, s, 3L);
+	    s += 3;
+	}
+	else
+	    add_char_buff(&redobuff, mb_cptr2char_adv(&s));
+    }
+}
+
+/*
  * Append a character to the redo buffer.
  * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters.
  */
@@ -3941,14 +3961,6 @@ getcmdkeycmd(
 	    if (c1 == K_ESC)
 		c1 = ESC;
 	}
-	if (c1 == Ctrl_V)
-	{
-	    // CTRL-V is followed by octal, hex or other characters, reverses
-	    // what AppendToRedobuffLit() does.
-	    ++no_reduce_keys;  //  don't merge modifyOtherKeys
-	    c1 = get_literal(TRUE);
-	    --no_reduce_keys;
-	}
 
 	if (got_int)
 	    aborted = TRUE;
@@ -3962,19 +3974,27 @@ getcmdkeycmd(
 	    emsg(_(e_cmd_mapping_must_end_with_cr_before_second_cmd));
 	    aborted = TRUE;
 	}
-	else if (IS_SPECIAL(c1))
+	else if (c1 == K_SNR)
 	{
-	    if (c1 == K_SNR)
-		ga_concat(&line_ga, (char_u *)"<SNR>");
-	    else
-	    {
-		semsg(e_cmd_mapping_must_not_include_str_key,
-					       get_special_key_name(c1, cmod));
-		aborted = TRUE;
-	    }
+	    ga_concat(&line_ga, (char_u *)"<SNR>");
 	}
 	else
-	    ga_append(&line_ga, c1);
+	{
+	    if (cmod != 0)
+	    {
+		ga_append(&line_ga, K_SPECIAL);
+		ga_append(&line_ga, KS_MODIFIER);
+		ga_append(&line_ga, cmod);
+	    }
+	    if (IS_SPECIAL(c1))
+	    {
+		ga_append(&line_ga, K_SPECIAL);
+		ga_append(&line_ga, K_SECOND(c1));
+		ga_append(&line_ga, K_THIRD(c1));
+	    }
+	    else
+		ga_append(&line_ga, c1);
+	}
 
 	cmod = 0;
     }
--- a/src/ops.c
+++ b/src/ops.c
@@ -3701,7 +3701,7 @@ do_pending_operator(cmdarg_T *cap, int o
 		    ResetRedobuff();
 		else
 		{
-		    AppendToRedobuffLit(repeat_cmdline, -1);
+		    AppendToRedobuffSpec(repeat_cmdline);
 		    AppendToRedobuff(NL_STR);
 		    VIM_CLEAR(repeat_cmdline);
 		}
--- a/src/proto/getchar.pro
+++ b/src/proto/getchar.pro
@@ -11,6 +11,7 @@ void saveRedobuff(save_redo_T *save_redo
 void restoreRedobuff(save_redo_T *save_redo);
 void AppendToRedobuff(char_u *s);
 void AppendToRedobuffLit(char_u *str, int len);
+void AppendToRedobuffSpec(char_u *s);
 void AppendCharToRedobuff(int c);
 void AppendNumberToRedobuff(long n);
 void stuffReadbuff(char_u *s);
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -1001,10 +1001,6 @@ func Test_map_cmdkey()
   call assert_fails('call feedkeys("\<F3>", "xt")', 'E1136:')
   call assert_equal(0, x)
 
-  noremap <F3> <Cmd><F3>let x = 2<CR>
-  call assert_fails('call feedkeys("\<F3>", "xt")', 'E1137:')
-  call assert_equal(0, x)
-
   noremap <F3> <Cmd>let x = 3
   call assert_fails('call feedkeys("\<F3>", "xt!")', 'E1255:')
   call assert_equal(0, x)
@@ -1104,11 +1100,6 @@ func Test_map_cmdkey()
   unmap <F3>
   unmap! <F3>
   %bw!
-
-  " command line ending in "0" is handled without errors
-  onoremap ix <cmd>eval 0<cr>
-  call feedkeys('dix.', 'xt')
-  ounmap ix
 endfunc
 
 " text object enters visual mode
@@ -1495,6 +1486,24 @@ func Test_map_cmdkey_redo()
   call delete('Xcmdtext')
   delfunc SelectDash
   ounmap i-
+
+  new
+  call setline(1, 'aaa bbb ccc ddd')
+
+  " command can contain special keys
+  onoremap ix <Cmd>let g:foo ..= '…'<Bar>normal! <C-Right><CR>
+  let g:foo = ''
+  call feedkeys('0dix.', 'xt')
+  call assert_equal('……', g:foo)
+  call assert_equal('ccc ddd', getline(1))
+  unlet g:foo
+
+  " command line ending in "0" is handled without errors
+  onoremap ix <Cmd>eval 0<CR>
+  call feedkeys('dix.', 'xt')
+
+  ounmap ix
+  bwipe!
 endfunc
 
 func Test_map_script_cmd_restore()
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1516,
+/**/
     1515,
 /**/
     1514,