changeset 18717:14d2a210fab1 v8.1.2350

patch 8.1.2350: other text for CTRL-V in Insert mode with modifyOtherKeys Commit: https://github.com/vim/vim/commit/fc4ea2a72d36de1196a3ce17352e72f8fe90f4bb Author: Bram Moolenaar <Bram@vim.org> Date: Tue Nov 26 19:33:22 2019 +0100 patch 8.1.2350: other text for CTRL-V in Insert mode with modifyOtherKeys Problem: Other text for CTRL-V in Insert mode with modifyOtherKeys. Solution: Convert the Escape sequence back to key as if modifyOtherKeys is not set, and use CTRL-SHIFT-V to get the Escape sequence itself. (closes #5254)
author Bram Moolenaar <Bram@vim.org>
date Tue, 26 Nov 2019 19:45:04 +0100
parents 9568bc68c45d
children 710efe6952f4
files runtime/doc/cmdline.txt runtime/doc/insert.txt src/edit.c src/ex_getln.c src/getchar.c src/proto/edit.pro src/proto/getchar.pro src/proto/term.pro src/term.c src/testdir/test_termcodes.vim src/version.c
diffstat 11 files changed, 182 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -77,10 +77,17 @@ CTRL-V		Insert next non-digit literally.
 		way as in Insert mode (see above, |i_CTRL-V|).
 		Note: Under Windows CTRL-V is often mapped to paste text.
 		Use CTRL-Q instead then.
+		When |modifyOtherKeys| is enabled then special Escape sequence
+		is converted back to what it was without |modifyOtherKeys|,
+		unless the Shift key is also pressed.
 							*c_CTRL-Q*
 CTRL-Q		Same as CTRL-V.  But with some terminals it is used for
 		control flow, it doesn't work then.
 
+CTRL-SHIFT-V				*c_CTRL-SHIFT-V* *c_CTRL-SHIFT-Q*
+CTRL-SHIFT-Q	Works just like CTRL-V, unless |modifyOtherKeys| is active,
+		then it inserts the Escape sequence for a key with modifiers.
+
 							*c_<Left>* *c_Left*
 <Left>		cursor left
 							*c_<Right>* *c_Right*
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -1,4 +1,4 @@
-*insert.txt*    For Vim version 8.1.  Last change: 2019 Oct 20
+*insert.txt*    For Vim version 8.1.  Last change: 2019 Nov 26
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -196,6 +196,13 @@ CTRL-V		Insert next non-digit literally.
 		mapping.
 		Note: When CTRL-V is mapped (e.g., to paste text) you can
 		often use CTRL-Q instead |i_CTRL-Q|.
+		When |modifyOtherKeys| is enabled then special Escape sequence
+		is converted back to what it was without |modifyOtherKeys|,
+		unless the Shift key is also pressed.
+
+						*i_CTRL-SHIFT-V*
+CTRL-SHIFT-V	Works just like CTRL-V, unless |modifyOtherKeys| is active,
+		then it inserts the Escape sequence for a key with modifiers.
 
 						*i_CTRL-Q*
 CTRL-Q		Same as CTRL-V.
--- a/src/edit.c
+++ b/src/edit.c
@@ -1531,6 +1531,7 @@ ins_ctrl_v(void)
 {
     int		c;
     int		did_putchar = FALSE;
+    int		prev_mod_mask = mod_mask;
 
     /* may need to redraw when no more chars available now */
     ins_redraw(FALSE);
@@ -1554,6 +1555,12 @@ ins_ctrl_v(void)
 #ifdef FEAT_CMDL_INFO
     clear_showcmd();
 #endif
+
+    if ((c == ESC || c == CSI) && !(prev_mod_mask & MOD_MASK_SHIFT))
+	// Using CTRL-V: Change any modifyOtherKeys ESC sequence to a normal
+	// key.  Don't do this for CTRL-SHIFT-V.
+	c = decodeModifyOtherKeys(c);
+
     insert_special(c, FALSE, TRUE);
 #ifdef FEAT_RIGHTLEFT
     revins_chars++;
@@ -1562,6 +1569,59 @@ ins_ctrl_v(void)
 }
 
 /*
+ * After getting an ESC or CSI for a literal key: If the typeahead buffer
+ * contains a modifyOtherKeys sequence then decode it and return the result.
+ * Otherwise return "c".
+ * Note that this doesn't wait for characters, they must be in the typeahead
+ * buffer already.
+ */
+    int
+decodeModifyOtherKeys(int c)
+{
+    char_u  *p = typebuf.tb_buf + typebuf.tb_off;
+    int	    idx;
+    int	    form = 0;
+    int	    argidx = 0;
+    int	    arg[2] = {0, 0};
+
+    // Recognize:
+    // form 0: {lead}{key};{modifier}u
+    // form 1: {lead}27;{modifier};{key}~
+    if ((c == CSI || (c == ESC && *p == '[')) && typebuf.tb_len >= 4)
+    {
+	idx = (*p == '[');
+	if (p[idx] == '2' && p[idx + 1] == '7' && p[idx + 2] == ';')
+	{
+	    form = 1;
+	    idx += 3;
+	}
+	while (idx < typebuf.tb_len && argidx < 2)
+	{
+	    if (p[idx] == ';')
+		++argidx;
+	    else if (VIM_ISDIGIT(p[idx]))
+		arg[argidx] = arg[argidx] * 10 + (p[idx] - '0');
+	    else
+		break;
+	    ++idx;
+	}
+	if (idx < typebuf.tb_len
+		&& p[idx] == (form == 1 ? '~' : 'u')
+		&& argidx == 1)
+	{
+	    // Match, consume the code.
+	    typebuf.tb_off += idx + 1;
+	    typebuf.tb_len -= idx + 1;
+
+	    mod_mask = decode_modifiers(arg[!form]);
+	    c = merge_modifyOtherKeys(arg[form]);
+	}
+    }
+
+    return c;
+}
+
+/*
  * Put a character directly onto the screen.  It's not stored in a buffer.
  * Used while handling CTRL-K, CTRL-V, etc. in Insert mode.
  */
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2208,18 +2208,31 @@ getcmdline_int(
 
 	case Ctrl_V:
 	case Ctrl_Q:
-		ignore_drag_release = TRUE;
-		putcmdline('^', TRUE);
-		c = get_literal();	    /* get next (two) character(s) */
-		do_abbr = FALSE;	    /* don't do abbreviation now */
-		extra_char = NUL;
-		/* may need to remove ^ when composing char was typed */
-		if (enc_utf8 && utf_iscomposing(c) && !cmd_silent)
 		{
-		    draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
-		    msg_putchar(' ');
-		    cursorcmd();
+		    int	 prev_mod_mask = mod_mask;
+
+		    ignore_drag_release = TRUE;
+		    putcmdline('^', TRUE);
+		    c = get_literal();	    // get next (two) character(s)
+		    do_abbr = FALSE;	    // don't do abbreviation now
+		    extra_char = NUL;
+		    // may need to remove ^ when composing char was typed
+		    if (enc_utf8 && utf_iscomposing(c) && !cmd_silent)
+		    {
+			draw_cmdline(ccline.cmdpos,
+						ccline.cmdlen - ccline.cmdpos);
+			msg_putchar(' ');
+			cursorcmd();
+		    }
+
+		    if ((c == ESC || c == CSI)
+					  && !(prev_mod_mask & MOD_MASK_SHIFT))
+			// Using CTRL-V: Change any modifyOtherKeys ESC
+			// sequence to a normal key.  Don't do this for
+			// CTRL-SHIFT-V.
+			c = decodeModifyOtherKeys(c);
 		}
+
 		break;
 
 #ifdef FEAT_DIGRAPHS
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -1525,6 +1525,38 @@ updatescript(int c)
 }
 
 /*
+ * Convert "c" plus "mod_mask" to merge the effect of modifyOtherKeys into the
+ * character.
+ */
+    int
+merge_modifyOtherKeys(int c_arg)
+{
+    int c = c_arg;
+
+    if (mod_mask & MOD_MASK_CTRL)
+    {
+	if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_'))
+	{
+	    c &= 0x1f;
+	    mod_mask &= ~MOD_MASK_CTRL;
+	}
+	else if (c == '6')
+	{
+	    // CTRL-6 is equivalent to CTRL-^
+	    c = 0x1e;
+	    mod_mask &= ~MOD_MASK_CTRL;
+	}
+    }
+    if ((mod_mask & (MOD_MASK_META | MOD_MASK_ALT))
+	    && c >= 0 && c <= 127)
+    {
+	c += 0x80;
+	mod_mask &= ~(MOD_MASK_META|MOD_MASK_ALT);
+    }
+    return c;
+}
+
+/*
  * Get the next input character.
  * Can return a special key or a multi-byte character.
  * Can return NUL when called recursively, use safe_vgetc() if that's not
@@ -1765,30 +1797,9 @@ vgetc(void)
 	    }
 
 	    if (!no_reduce_keys)
-	    {
 		// A modifier was not used for a mapping, apply it to ASCII
 		// keys.  Shift would already have been applied.
-		if (mod_mask & MOD_MASK_CTRL)
-		{
-		    if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_'))
-		    {
-			c &= 0x1f;
-			mod_mask &= ~MOD_MASK_CTRL;
-		    }
-		    else if (c == '6')
-		    {
-			// CTRL-6 is equivalent to CTRL-^
-			c = 0x1e;
-			mod_mask &= ~MOD_MASK_CTRL;
-		    }
-		}
-		if ((mod_mask & (MOD_MASK_META | MOD_MASK_ALT))
-			&& c >= 0 && c <= 127)
-		{
-		    c += 0x80;
-		    mod_mask &= ~(MOD_MASK_META|MOD_MASK_ALT);
-		}
-	    }
+		c = merge_modifyOtherKeys(c);
 
 	    break;
 	}
--- a/src/proto/edit.pro
+++ b/src/proto/edit.pro
@@ -2,6 +2,7 @@
 int edit(int cmdchar, int startln, long count);
 int ins_need_undo_get(void);
 void ins_redraw(int ready);
+int decodeModifyOtherKeys(int c);
 void edit_putchar(int c, int highlight);
 char_u *prompt_text(void);
 int prompt_curpos_editable(void);
--- a/src/proto/getchar.pro
+++ b/src/proto/getchar.pro
@@ -37,6 +37,7 @@ void openscript(char_u *name, int direct
 void close_all_scripts(void);
 int using_script(void);
 void before_blocking(void);
+int merge_modifyOtherKeys(int c_arg);
 int vgetc(void);
 int safe_vgetc(void);
 int plain_vgetc(void);
--- a/src/proto/term.pro
+++ b/src/proto/term.pro
@@ -65,6 +65,7 @@ int get_termcode_len(int idx);
 void del_termcode(char_u *name);
 void set_mouse_topline(win_T *wp);
 int is_mouse_topline(win_T *wp);
+int decode_modifiers(int n);
 int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen);
 void term_get_fg_color(char_u *r, char_u *g, char_u *b);
 void term_get_bg_color(char_u *r, char_u *g, char_u *b);
--- a/src/term.c
+++ b/src/term.c
@@ -4277,7 +4277,7 @@ put_string_in_typebuf(
 /*
  * Decode a modifier number as xterm provides it into MOD_MASK bits.
  */
-    static int
+    int
 decode_modifiers(int n)
 {
     int	    code = n - 1;
--- a/src/testdir/test_termcodes.vim
+++ b/src/testdir/test_termcodes.vim
@@ -1349,3 +1349,48 @@ func Test_mapping_works_with_shift_ctrl_
   call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S-A', 8)
   call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S-A', 8)
 endfunc
+
+func Test_insert_literal()
+  set timeoutlen=10
+  new
+  " CTRL-V CTRL-X inserts a ^X
+  call feedkeys('a' .. GetEscCodeCSIu('V', '5') .. GetEscCodeCSIu('X', '5') .. "\<Esc>", 'Lx!')
+  call assert_equal("\<C-X>", getline(1))
+
+  call setline(1, '')
+  call feedkeys('a' .. GetEscCodeCSI27('V', '5') .. GetEscCodeCSI27('X', '5') .. "\<Esc>", 'Lx!')
+  call assert_equal("\<C-X>", getline(1))
+
+  " CTRL-SHIFT-V CTRL-X inserts escape sequencd
+  call setline(1, '')
+  call feedkeys('a' .. GetEscCodeCSIu('V', '6') .. GetEscCodeCSIu('X', '5') .. "\<Esc>", 'Lx!')
+  call assert_equal("\<Esc>[88;5u", getline(1))
+
+  call setline(1, '')
+  call feedkeys('a' .. GetEscCodeCSI27('V', '6') .. GetEscCodeCSI27('X', '5') .. "\<Esc>", 'Lx!')
+  call assert_equal("\<Esc>[27;5;88~", getline(1))
+
+  bwipe!
+  set timeoutlen&
+endfunc
+
+func Test_cmdline_literal()
+  set timeoutlen=10
+
+  " CTRL-V CTRL-Y inserts a ^Y
+  call feedkeys(':' .. GetEscCodeCSIu('V', '5') .. GetEscCodeCSIu('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
+  call assert_equal("\"\<C-Y>", @:)
+
+  call feedkeys(':' .. GetEscCodeCSI27('V', '5') .. GetEscCodeCSI27('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
+  call assert_equal("\"\<C-Y>", @:)
+
+  " CTRL-SHIFT-V CTRL-Y inserts escape sequencd
+  call feedkeys(':' .. GetEscCodeCSIu('V', '6') .. GetEscCodeCSIu('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
+  call assert_equal("\"\<Esc>[89;5u", @:)
+
+  call setline(1, '')
+  call feedkeys(':' .. GetEscCodeCSI27('V', '6') .. GetEscCodeCSI27('Y', '5') .. "\<C-B>\"\<CR>", 'Lx!')
+  call assert_equal("\"\<Esc>[27;5;89~", @:)
+
+  set timeoutlen&
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -738,6 +738,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2350,
+/**/
     2349,
 /**/
     2348,