changeset 10640:27be410d6d29 v8.0.0210

patch 8.0.0210: no support for bracketed paste commit https://github.com/vim/vim/commit/ec2da36ca48b40c0654b32a8d2c9f52e796daa5e Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jan 21 20:04:22 2017 +0100 patch 8.0.0210: no support for bracketed paste Problem: Vim does not support bracketed paste, as implemented by xterm and other terminals. Solution: Add t_BE, t_BD, t_PS and t_PE.
author Christian Brabandt <cb@256bit.org>
date Sat, 21 Jan 2017 20:15:04 +0100
parents 9b3141c5aa1b
children bcd353707cfc
files runtime/doc/term.txt src/edit.c src/evalfunc.c src/getchar.c src/keymap.h src/misc2.c src/normal.c src/option.c src/proto/edit.pro src/term.c src/term.h src/version.c src/vim.h
diffstat 13 files changed, 185 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -89,6 +89,18 @@ an external command (e.g., "!!"), the te
 for a moment.  This means that you can stop the output to the screen by
 hitting a printing key.  Output resumes when you hit <BS>.
 
+						*xterm-bracketed-paste*
+When the 't_BE' option is set then 't_BE' will be sent to the
+terminal when entering "raw" mode and 't_BD' when leaving "raw" mode.  The
+terminal is then expected to put 't_PS' before pasted text and 't_PE' after
+pasted text.  This way Vim can separate text that is pasted from characters
+that are typed.  The pasted text is handled like when the middle mouse button
+is used.
+
+Note that in some situations Vim will not recognize the bracketed paste and
+you will get the raw text.  In other situations Vim will only get the first
+pasted character and drop the rest, e.g. when using the "r" command.
+
 							*cs7-problem*
 Note: If the terminal settings are changed after running Vim, you might have
 an illegal combination of settings.  This has been reported on Solaris 2.5
@@ -306,6 +318,10 @@ Added by Vim (there are no standard code
 		|xterm-true-color|
 	t_8b	set background color (R, G, B)			*t_8b* *'t_8b'*
 		|xterm-true-color|
+	t_BE	enable bracketed paste mode			*t_BE* *'t_BE'*
+		|xterm-bracketed-paste|
+	t_BD	disable bracketed paste mode			*t_BD* *'t_BD'*
+		|xterm-bracketed-paste|
 
 KEY CODES
 Note: Use the <> form if possible
@@ -398,6 +414,8 @@ Note: Use the <> form if possible
 	t_KK	<k8>		keypad 8		 *<k8>*	*t_KK* *'t_KK'*
 	t_KL	<k9>		keypad 9		 *<k9>*	*t_KL* *'t_KL'*
 		<Mouse>		leader of mouse code		*<Mouse>*
+	t_PS	start of brackted paste |xterm-bracketed-paste|   *t_PS* 't_PS'
+	t_PE	end of bracketed paste |xterm-bracketed-paste|    *t_PE* 't_PE'
 
 Note about t_so and t_mr: When the termcap entry "so" is not present the
 entry for "mr" is used.  And vice versa.  The same is done for "se" and "me".
--- a/src/edit.c
+++ b/src/edit.c
@@ -309,6 +309,7 @@ static int	dont_sync_undo = FALSE;	/* CT
  * "cmdchar" can be:
  * 'i'	normal insert command
  * 'a'	normal append command
+ * K_PS bracketed paste
  * 'R'	replace command
  * 'r'	"r<CR>" command: insert one <CR>.  Note: count can be > 1, for redo,
  *	but still only one <CR> is inserted.  The <Esc> is not used for redo.
@@ -782,10 +783,14 @@ edit(
 	    dont_sync_undo = TRUE;
 	else
 	    dont_sync_undo = FALSE;
-	do
-	{
-	    c = safe_vgetc();
-	} while (c == K_IGNORE);
+	if (cmdchar == K_PS)
+	    /* Got here from normal mode when bracketed paste started. */
+	    c = K_PS;
+	else
+	    do
+	    {
+		c = safe_vgetc();
+	    } while (c == K_IGNORE);
 
 #ifdef FEAT_AUTOCMD
 	/* Don't want K_CURSORHOLD for the second key, e.g., after CTRL-V. */
@@ -1193,6 +1198,16 @@ doESCkey:
 	    ins_mousescroll(MSCR_RIGHT);
 	    break;
 #endif
+	case K_PS:
+	    bracketed_paste(PASTE_INSERT, FALSE, NULL);
+	    if (cmdchar == K_PS)
+		/* invoked from normal mode, bail out */
+		goto doESCkey;
+	    break;
+	case K_PE:
+	    /* Got K_PE without K_PS, ignore. */
+	    break;
+
 #ifdef FEAT_GUI_TABLINE
 	case K_TABLINE:
 	case K_TABMENU:
@@ -9424,6 +9439,91 @@ ins_mousescroll(int dir)
 }
 #endif
 
+/*
+ * Handle receiving P_PS: start paste mode.  Inserts the following text up to
+ * P_PE literally.
+ * When "drop" is TRUE then consume the text and drop it.
+ */
+    int
+bracketed_paste(paste_mode_T mode, int drop, garray_T *gap)
+{
+    int		c;
+    char_u	buf[NUMBUFLEN + MB_MAXBYTES];
+    int		idx = 0;
+    char_u	*end = find_termcode((char_u *)"PE");
+    int		ret_char = -1;
+    int		save_allow_keys = allow_keys;
+
+    /* If the end code is too long we can't detect it, read everything. */
+    if (STRLEN(end) >= NUMBUFLEN)
+	end = NULL;
+    ++no_mapping;
+    allow_keys = 0;
+    for (;;)
+    {
+	/* When the end is not defined read everything. */
+	if (end == NULL && vpeekc() == NUL)
+	    break;
+	c = plain_vgetc();
+#ifdef FEAT_MBYTE
+	if (has_mbyte)
+	    idx += (*mb_char2bytes)(c, buf + idx);
+	else
+#endif
+	    buf[idx++] = c;
+	buf[idx] = NUL;
+	if (end != NUL && STRNCMP(buf, end, idx) == 0)
+	{
+	    if (end[idx] == NUL)
+		break; /* Found the end of paste code. */
+	    continue;
+	}
+	if (!drop)
+	{
+	    switch (mode)
+	    {
+		case PASTE_CMDLINE:
+		    put_on_cmdline(buf, idx, TRUE);
+		    break;
+
+		case PASTE_EX:
+		    if (gap != NULL && ga_grow(gap, idx) == OK)
+		    {
+			mch_memmove((char *)gap->ga_data + gap->ga_len,
+							     buf, (size_t)idx);
+			gap->ga_len += idx;
+		    }
+		    break;
+
+		case PASTE_INSERT:
+		    if (stop_arrow() == OK)
+		    {
+			ins_char_bytes(buf, idx);
+			AppendToRedobuffLit(buf, idx);
+		    }
+		    break;
+
+		case PASTE_ONE_CHAR:
+		    if (ret_char == -1)
+		    {
+#ifdef FEAT_MBYTE
+			if (has_mbyte)
+			    ret_char = (*mb_ptr2char)(buf);
+			else
+#endif
+			    ret_char = buf[0];
+		    }
+		    break;
+	    }
+	}
+	idx = 0;
+    }
+    --no_mapping;
+    allow_keys = save_allow_keys;
+
+    return ret_char;
+}
+
 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
     static void
 ins_tabline(int c)
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -4231,7 +4231,7 @@ f_getchar(typval_T *argvars, typval_T *r
     {
 	if (argvars[0].v_type == VAR_UNKNOWN)
 	    /* getchar(): blocking wait. */
-	    n = safe_vgetc();
+	    n = plain_vgetc();
 	else if (get_tv_number_chk(&argvars[0], &error) == 1)
 	    /* getchar(1): only check if char avail */
 	    n = vpeekc_any();
@@ -4240,7 +4240,7 @@ f_getchar(typval_T *argvars, typval_T *r
 	    n = 0;
 	else
 	    /* getchar(0) and char avail: return char */
-	    n = safe_vgetc();
+	    n = plain_vgetc();
 
 	if (n == K_IGNORE)
 	    continue;
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -1817,6 +1817,12 @@ plain_vgetc(void)
     {
 	c = safe_vgetc();
     } while (c == K_IGNORE || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR);
+
+    if (c == K_PS)
+	/* Only handle the first pasted character.  Drop the rest, since we
+	 * don't know what to do with it. */
+	c = bracketed_paste(PASTE_ONE_CHAR, FALSE, NULL);
+
     return c;
 }
 
@@ -1906,7 +1912,7 @@ vungetc(int c)
 }
 
 /*
- * get a character:
+ * Get a character:
  * 1. from the stuffbuffer
  *	This is used for abbreviated commands like "D" -> "d$".
  *	Also used to redo a command for ".".
--- a/src/keymap.h
+++ b/src/keymap.h
@@ -391,6 +391,8 @@ enum key_extra
 #define K_KMULTIPLY	TERMCAP2KEY('K', '9')	/* keypad * */
 #define K_KENTER	TERMCAP2KEY('K', 'A')	/* keypad Enter */
 #define K_KPOINT	TERMCAP2KEY('K', 'B')	/* keypad . or ,*/
+#define K_PS		TERMCAP2KEY('P', 'S')	/* paste start */
+#define K_PE		TERMCAP2KEY('P', 'E')	/* paste end */
 
 #define K_K0		TERMCAP2KEY('K', 'C')	/* keypad 0 */
 #define K_K1		TERMCAP2KEY('K', 'D')	/* keypad 1 */
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -2294,6 +2294,8 @@ static struct key_name_entry
     {K_XDOWN,		(char_u *)"xDown"},
     {K_XLEFT,		(char_u *)"xLeft"},
     {K_XRIGHT,		(char_u *)"xRight"},
+    {K_PS,		(char_u *)"PasteStart"},
+    {K_PE,		(char_u *)"PasteEnd"},
 
     {K_F1,		(char_u *)"F1"},
     {K_F2,		(char_u *)"F2"},
--- a/src/normal.c
+++ b/src/normal.c
@@ -426,6 +426,7 @@ static const struct nv_cmd
 #ifdef FEAT_AUTOCMD
     {K_CURSORHOLD, nv_cursorhold, NV_KEEPREG,		0},
 #endif
+    {K_PS,	nv_edit,	0,			0},
 };
 
 /* Number of commands in nv_cmds[]. */
@@ -3858,7 +3859,7 @@ add_to_showcmd(int c)
 	K_VER_SCROLLBAR, K_HOR_SCROLLBAR,
 	K_LEFTMOUSE_NM, K_LEFTRELEASE_NM,
 # endif
-	K_IGNORE,
+	K_IGNORE, K_PS,
 	K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE,
 	K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
 	K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
@@ -9015,6 +9016,7 @@ nv_esc(cmdarg_T *cap)
 
 /*
  * Handle "A", "a", "I", "i" and <Insert> commands.
+ * Also handle K_PS, start bracketed paste.
  */
     static void
 nv_edit(cmdarg_T *cap)
@@ -9042,6 +9044,9 @@ nv_edit(cmdarg_T *cap)
 	/* Only give this error when 'insertmode' is off. */
 	EMSG(_(e_modifiable));
 	clearop(cap->oap);
+	if (cap->cmdchar == K_PS)
+	    /* drop the pasted text */
+	    bracketed_paste(PASTE_INSERT, TRUE, NULL);
     }
     else if (!checkclearopq(cap->oap))
     {
@@ -9073,6 +9078,7 @@ nv_edit(cmdarg_T *cap)
 		break;
 
 	    case 'a':	/* "a"ppend is like "i"nsert on the next character. */
+	    case K_PS:	/* bracketed paste works like "a"ppend */
 #ifdef FEAT_VIRTUALEDIT
 		/* increment coladd when in virtual space, increment the
 		 * column otherwise, also to append after an unprintable char */
@@ -9103,6 +9109,9 @@ nv_edit(cmdarg_T *cap)
 
 	invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
     }
+    else if (cap->cmdchar == K_PS)
+	/* drop the pasted text */
+	bracketed_paste(PASTE_INSERT, TRUE, NULL);
 }
 
 /*
--- a/src/option.c
+++ b/src/option.c
@@ -3040,6 +3040,8 @@ static struct vimoption options[] =
     p_term("t_ZR", T_CZR)
     p_term("t_8f", T_8F)
     p_term("t_8b", T_8B)
+    p_term("t_BE", T_BE)
+    p_term("t_BD", T_BD)
 
 /* terminal key codes are not in here */
 
--- a/src/proto/edit.pro
+++ b/src/proto/edit.pro
@@ -38,6 +38,7 @@ void fixthisline(int (*get_the_indent)(v
 void fix_indent(void);
 int in_cinkeys(int keytyped, int when, int line_is_empty);
 int hkmap(int c);
+int bracketed_paste(paste_mode_T mode, int drop, garray_T *gap);
 void ins_scroll(void);
 void ins_horscroll(void);
 int ins_copychar(linenr_T lnum);
--- a/src/term.c
+++ b/src/term.c
@@ -857,6 +857,8 @@ static struct builtin_term builtin_termc
     {(int)KS_8F,	IF_EB("\033[38;2;%lu;%lu;%lum", ESC_STR "[38;2;%lu;%lu;%lum")},
     {(int)KS_8B,	IF_EB("\033[48;2;%lu;%lu;%lum", ESC_STR "[48;2;%lu;%lu;%lum")},
 #  endif
+    {(int)KS_CBE,	IF_EB("\033[?2004h", ESC_STR "[?2004h")},
+    {(int)KS_CBD,	IF_EB("\033[?2004l", ESC_STR "[?2004l")},
 
     {K_UP,		IF_EB("\033O*A", ESC_STR "O*A")},
     {K_DOWN,		IF_EB("\033O*B", ESC_STR "O*B")},
@@ -902,13 +904,15 @@ static struct builtin_term builtin_termc
     {K_ZEND,		IF_EB("\033[8;*~", ESC_STR "[8;*~")},
     {K_PAGEUP,		IF_EB("\033[5;*~", ESC_STR "[5;*~")},
     {K_PAGEDOWN,	IF_EB("\033[6;*~", ESC_STR "[6;*~")},
-    {K_KPLUS,		IF_EB("\033O*k", ESC_STR "O*k")},	/* keypad plus */
-    {K_KMINUS,		IF_EB("\033O*m", ESC_STR "O*m")},	/* keypad minus */
-    {K_KDIVIDE,		IF_EB("\033O*o", ESC_STR "O*o")},	/* keypad / */
-    {K_KMULTIPLY,	IF_EB("\033O*j", ESC_STR "O*j")},	/* keypad * */
-    {K_KENTER,		IF_EB("\033O*M", ESC_STR "O*M")},	/* keypad Enter */
-    {K_KPOINT,		IF_EB("\033O*n", ESC_STR "O*n")},	/* keypad . */
-    {K_KDEL,		IF_EB("\033[3;*~", ESC_STR "[3;*~")},	/* keypad Del */
+    {K_KPLUS,		IF_EB("\033O*k", ESC_STR "O*k")},     /* keypad plus */
+    {K_KMINUS,		IF_EB("\033O*m", ESC_STR "O*m")},     /* keypad minus */
+    {K_KDIVIDE,		IF_EB("\033O*o", ESC_STR "O*o")},     /* keypad / */
+    {K_KMULTIPLY,	IF_EB("\033O*j", ESC_STR "O*j")},     /* keypad * */
+    {K_KENTER,		IF_EB("\033O*M", ESC_STR "O*M")},     /* keypad Enter */
+    {K_KPOINT,		IF_EB("\033O*n", ESC_STR "O*n")},     /* keypad . */
+    {K_KDEL,		IF_EB("\033[3;*~", ESC_STR "[3;*~")}, /* keypad Del */
+    {K_PS,		IF_EB("\033[200~", ESC_STR "[200~")}, /* paste start */
+    {K_PE,		IF_EB("\033[201~", ESC_STR "[201~")}, /* paste end */
 
     {BT_EXTRA_KEYS,   ""},
     {TERMCAP2KEY('k', '0'), IF_EB("\033[10;*~", ESC_STR "[10;*~")}, /* F0 */
@@ -1224,6 +1228,8 @@ static struct builtin_term builtin_termc
     {K_KMULTIPLY,	"[KMULTIPLY]"},
     {K_KENTER,		"[KENTER]"},
     {K_KPOINT,		"[KPOINT]"},
+    {K_PS,		"[PASTE-START]"},
+    {K_PE,		"[PASTE-END]"},
     {K_K0,		"[K0]"},
     {K_K1,		"[K1]"},
     {K_K2,		"[K2]"},
@@ -1538,6 +1544,8 @@ set_termname(char_u *term)
 				{KS_CSI, "SI"}, {KS_CEI, "EI"},
 				{KS_U7, "u7"}, {KS_RBG, "RB"},
 				{KS_8F, "8f"}, {KS_8B, "8b"},
+				{KS_CBE, "BE"}, {KS_CBD, "BD"},
+				{KS_CPS, "PS"}, {KS_CPE, "PE"},
 				{(enum SpecialKey)0, NULL}
 			    };
 
@@ -3140,6 +3148,7 @@ starttermcap(void)
     {
 	out_str(T_TI);			/* start termcap mode */
 	out_str(T_KS);			/* start "keypad transmit" mode */
+	out_str(T_BE);			/* enable bracketed paste moe */
 	out_flush();
 	termcap_active = TRUE;
 	screen_start();			/* don't know where cursor is now */
@@ -3189,6 +3198,7 @@ stoptermcap(void)
 	    check_for_codes_from_term();
 	}
 #endif
+	out_str(T_BD);			/* disable bracketed paste moe */
 	out_str(T_KE);			/* stop "keypad transmit" mode */
 	out_flush();
 	termcap_active = FALSE;
--- a/src/term.h
+++ b/src/term.h
@@ -89,10 +89,14 @@ enum SpecialKey
     KS_OP,	/* original color pair */
     KS_U7,	/* request cursor position */
     KS_8F,	/* set foreground color (RGB) */
-    KS_8B	/* set background color (RGB) */
+    KS_8B,	/* set background color (RGB) */
+    KS_CBE,	/* enable bracketed paste mode */
+    KS_CBD,	/* disable bracketed paste mode */
+    KS_CPS,	/* start of brackted paste */
+    KS_CPE	/* end of brackted paste */
 };
 
-#define KS_LAST	    KS_8B
+#define KS_LAST	    KS_CPE
 
 /*
  * the terminal capabilities are stored in this array
@@ -170,6 +174,10 @@ extern char_u *(term_strings[]);    /* c
 #define T_U7	(term_str(KS_U7))	/* request cursor position */
 #define T_8F	(term_str(KS_8F))	/* set foreground color (RGB) */
 #define T_8B	(term_str(KS_8B))	/* set background color (RGB) */
+#define T_BE	(term_str(KS_CBE))	/* enable bracketed paste mode */
+#define T_BD	(term_str(KS_CBD))	/* disable bracketed paste mode */
+#define T_PS	(term_str(KS_CPS))	/* start of bracketed paste */
+#define T_PE	(term_str(KS_CPE))	/* end of bracketed paste */
 
 #define TMODE_COOK  0	/* terminal mode for external cmds and Ex mode */
 #define TMODE_SLEEP 1	/* terminal mode for sleeping (cooked but no echo) */
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    210,
+/**/
     209,
 /**/
     208,
--- a/src/vim.h
+++ b/src/vim.h
@@ -2108,6 +2108,14 @@ typedef enum
     ASSERT_OTHER
 } assert_type_T;
 
+/* Mode for bracketed_paste(). */
+typedef enum {
+    PASTE_INSERT,	/* insert mode */
+    PASTE_CMDLINE,	/* command line */
+    PASTE_EX,		/* ex mode line */
+    PASTE_ONE_CHAR	/* return first character */
+} paste_mode_T;
+
 #include "ex_cmds.h"	    /* Ex command defines */
 #include "spell.h"	    /* spell checking stuff */