about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Kiddle <opk@zsh.org>2015-06-19 00:15:38 +0200
committerOliver Kiddle <opk@zsh.org>2015-06-19 00:15:38 +0200
commit98687fa1dec803f041cbb5417c146d8aa5129b53 (patch)
treee62c09baa6ea0717677e7bad7adf3a64423cba5a
parent0a0ba5e6641a8a78d745928e5738c95cc5353ee0 (diff)
downloadzsh-98687fa1dec803f041cbb5417c146d8aa5129b53.tar.gz
zsh-98687fa1dec803f041cbb5417c146d8aa5129b53.tar.xz
zsh-98687fa1dec803f041cbb5417c146d8aa5129b53.zip
35474, 35492: support the bracketed paste mode of newer terminal emulators
-rw-r--r--ChangeLog5
-rw-r--r--Doc/Zsh/params.yo17
-rw-r--r--Doc/Zsh/zle.yo12
-rw-r--r--Src/Zle/complist.c79
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle_hist.c15
-rw-r--r--Src/Zle/zle_keymap.c5
-rw-r--r--Src/Zle/zle_main.c15
-rw-r--r--Src/Zle/zle_misc.c68
9 files changed, 181 insertions, 36 deletions
diff --git a/ChangeLog b/ChangeLog
index 0a0bd36a9..d42fae3f1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2015-06-18  Oliver Kiddle  <opk@zsh.org>
 
+	* 35474, 35492: Doc/Zsh/params.yo, Doc/Zsh/zle.yo,
+	Src/Zle/complist.c, Src/Zle/iwidgets.list, Src/Zle/zle_hist.c,
+	Src/Zle/zle_keymap.c, Src/Zle/zle_main.c: support the
+	bracketed paste mode of newer terminal emulators
+
 	* 35487, 35496: Doc/Zsh/zle.yo, Src/Zle/complist.c,
 	Src/Zle/zle_hist.c: don't reinstate previous incremental search
 	string when search direction changes
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index eb3eb367e..e2091624b 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1642,6 +1642,23 @@ item(tt(ZDOTDIR))(
 The directory to search for shell startup files (.zshrc, etc),
 if not tt($HOME).
 )
+vindex(zle_bracketed_paste)
+cindex(bracketed paste)
+cindex(enabling bracketed paste)
+item(tt(zle_bracketed_paste))(
+Many terminal emulators have a feature that allows applications to
+identify when text is pasted into the terminal rather than being typed
+normally. For ZLE, this means that special characters such as tabs
+and newlines can be inserted instead of invoking editor commands.
+Furthermore, pasted text forms a single undo event and if the region is
+active, pasted text will replace the region.
+
+This two-element array contains the terminal escape sequences for
+enabling and disabling the feature. These escape sequences are used to
+enable bracketed paste when ZLE is active and disable it at other times.
+Unsetting the parameter has the effect of ensuring that bracketed paste
+remains disabled.
+)
 vindex(ZLE_LINE_ABORTED)
 item(tt(ZLE_LINE_ABORTED))(
 This parameter is set by the line editor when an error occurs.  It
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index a89f566c3..d3f067031 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -2059,6 +2059,18 @@ tindex(beep)
 item(tt(beep))(
 Beep, unless the tt(BEEP) option is unset.
 )
+tindex(bracketed-paste)
+item(tt(bracketed-paste))(
+This widget is invoked when text is pasted to the terminal emulator. It
+is not intended to be bound to actual keys but instead to the special
+sequence generated by the terminal emulator when text is pasted.
+If a numeric argument is given, shell quoting will be applied to the
+pasted text before it is inserted. When called from a widget function,
+an argument can be given to specify a variable to which pasted text is
+assigned.
+
+See also the tt(zle_bracketed_paste) parameter.
+)
 tindex(vi-cmd-mode)
 item(tt(vi-cmd-mode) (tt(^X^V)) (unbound) (tt(^[)))(
 Enter command mode; that is, select the `tt(vicmd)' keymap.
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 39c0c314d..0f73181f3 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -2269,41 +2269,16 @@ msearchpop(int *backp)
 }
 
 static Cmatch **
-msearch(Cmatch **ptr, int ins, int back, int rep, int *wrapp)
+msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp)
 {
-#ifdef MULTIBYTE_SUPPORT
-    /* MB_CUR_MAX may not be constant */
-    VARARR(char, s, MB_CUR_MAX+1);
-#else
-    char s[2];
-#endif
     Cmatch **p, *l = NULL, m;
     int x = mcol, y = mline;
     int ex, ey, wrap = 0, owrap = (msearchstate & MS_WRAPPED);
 
     msearchpush(ptr, back);
 
-    if (ins) {
-#ifdef MULTIBYTE_SUPPORT
-	if (lastchar_wide_valid)
-	{
-	    mbstate_t mbs;
-	    int len;
-
-	    memset(&mbs, 0, sizeof(mbs));
-	    len = wcrtomb(s, lastchar_wide, &mbs);
-	    if (len < 0)
-		len = 0;
-	    s[len] = '\0';
-	} else
-#endif
-	{
-	    s[0] = lastchar;
-	    s[1] = '\0';
-	}
-
-        msearchstr = dyncat(msearchstr, s);
-    }
+    if (ins)
+        msearchstr = dyncat(msearchstr, ins);
     if (back) {
         ex = mcols - 1;
         ey = -1;
@@ -3273,14 +3248,23 @@ domenuselect(Hookdef dummy, Chdata dat)
                    cmd == Th(z_historyincrementalsearchbackward) ||
                    ((mode == MM_FSEARCH || mode == MM_BSEARCH) &&
                     (cmd == Th(z_selfinsert) ||
-                     cmd == Th(z_selfinsertunmeta)))) {
+                     cmd == Th(z_selfinsertunmeta) ||
+		     cmd == Th(z_bracketedpaste)))) {
             Cmatch **np, **op = p;
             int was = (mode == MM_FSEARCH || mode == MM_BSEARCH);
-            int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta));
+            int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta) ||
+		cmd == Th(z_bracketedpaste));
             int back = (cmd == Th(z_historyincrementalsearchbackward));
             int wrap;
 
             do {
+		char *toins = NULL;
+#ifdef MULTIBYTE_SUPPORT
+		/* MB_CUR_MAX may not be constant */
+		VARARR(char, insert, MB_CUR_MAX+1);
+#else
+		char insert[2];
+#endif
                 if (was) {
                     p += wishcol - mcol;
                     mcol = wishcol;
@@ -3297,16 +3281,41 @@ domenuselect(Hookdef dummy, Chdata dat)
                         msearchstack = NULL;
 			msearchstate = MS_OK;
                     }
-                }
-                if (cmd == Th(z_selfinsertunmeta)) {
-		    fixunmeta();
-                }
+                } else {
+		    if (cmd == Th(z_selfinsertunmeta)) {
+			fixunmeta();
+		    }
+		    if (cmd == Th(z_bracketedpaste)) {
+			toins = bracketedstring();
+		    } else {
+			toins = insert;
+#ifdef MULTIBYTE_SUPPORT
+			if (lastchar_wide_valid)
+			{
+			    mbstate_t mbs;
+			    int len;
+
+			    memset(&mbs, 0, sizeof(mbs));
+			    len = wcrtomb(s, lastchar_wide, &mbs);
+			    if (len < 0)
+				len = 0;
+			    insert[len] = '\0';
+			} else
+#endif
+			{
+			    insert[0] = lastchar;
+			    insert[1] = '\0';
+			}
+		    }
+		}
                 wrap = 0;
-                np = msearch(p, ins, (ins ? (mode == MM_BSEARCH) : back),
+                np = msearch(p, toins, (ins ? (mode == MM_BSEARCH) : back),
                              (was && !ins), &wrap);
 
                 if (!ins)
                     mode = (back ? MM_BSEARCH : MM_FSEARCH);
+		else if (cmd == Th(z_bracketedpaste))
+		    free(toins);
 
                 if (*msearchstr) {
                     zsfree(lastsearch);
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index b41661a7d..6a07212d0 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -28,6 +28,7 @@
 "beginning-of-history", beginningofhistory, 0
 "beginning-of-line", beginningofline, 0
 "beginning-of-line-hist", beginningoflinehist, 0
+"bracketed-paste", bracketedpaste, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 0b3b9e7b7..ffb7ce98f 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -1620,6 +1620,21 @@ doisearch(char **args, int dir, int pattern)
 		feep = 1;
 	    else
 		goto ins;
+	} else if (cmd == Th(z_bracketedpaste)) {
+	    char *paste = bracketedstring();
+	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
+			   zlemetacs, sbptr, dir, nomatch);
+	    size_t pastelen = strlen(paste);
+	    if (sbptr + pastelen >= sibuf - FIRST_SEARCH_CHAR - 2) {
+		int oldsize = sibuf;
+		sibuf += (pastelen >= sibuf) ? pastelen + 1 : sibuf;
+		ibuf = hrealloc(ibuf, oldsize, sibuf);
+		sbuf = ibuf + FIRST_SEARCH_CHAR;
+	    }
+	    strcpy(sbuf + sbptr, paste);
+	    sbptr += pastelen;
+	    patprog = NULL;
+	    free(paste);
 	} else if (cmd == Th(z_acceptsearch)) {
 	    break;
 	} else {
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index c6fae251d..d355f41a4 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -1400,6 +1400,11 @@ default_bindings(void)
     bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL);
     bindkey(emap, "\30=",   refthingy(t_whatcursorposition), NULL);
 
+    /* bracketed paste applicable to all keymaps */
+    bindkey(emap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+    bindkey(vmap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+    bindkey(amap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+
     /* emacs mode: ESC sequences, all taken from the meta binding table */
     buf[0] = '\33';
     buf[2] = 0;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index cec44c0ed..7ccfb686a 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1119,7 +1119,7 @@ zlecore(void)
 char *
 zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 {
-    char *s;
+    char *s, **bracket;
     int old_errno = errno;
     int tmout = getiparam("TMOUT");
 
@@ -1248,6 +1248,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 
     zlecallhook(init, NULL);
 
+    if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+	fputs(*bracket, shout);
+
     zrefresh();
 
     zlecore();
@@ -1257,6 +1260,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 		  "ZLE_VARED_ABORTED" :
 		  "ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
 
+    if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+	fputs(bracket[1], shout);
+
     if (done && !exit_pending && !errflag)
 	zlecallhook(finish, NULL);
 
@@ -2004,6 +2010,8 @@ static struct features module_features = {
 int
 setup_(UNUSED(Module m))
 {
+    char **bpaste;
+
     /* Set up editor entry points */
     zle_entry_ptr = zle_main_entry;
     zle_load_state = 1;
@@ -2028,6 +2036,11 @@ setup_(UNUSED(Module m))
 
     clwords = (char **) zshcalloc((clwsize = 16) * sizeof(char *));
 
+    bpaste = zshcalloc(3*sizeof(char *));
+    bpaste[0] = ztrdup("\033[?2004h");
+    bpaste[1] = ztrdup("\033[?2004l");
+    setaparam("zle_bracketed_paste", bpaste);
+
     return 0;
 }
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4669ef2ad..c2fb2e7f4 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -736,6 +736,58 @@ yankpop(UNUSED(char **args))
 }
 
 /**/
+char *
+bracketedstring()
+{
+    static const char endesc[] = "\033[201~";
+    int endpos = 0;
+    size_t psize = 64;
+    char *pbuf = zalloc(psize);
+    size_t current = 0;
+    int next, timeout;
+
+    while (endesc[endpos]) {
+	if (current + 1 >= psize)
+	    pbuf = zrealloc(pbuf, psize *= 2);
+	if ((next = getbyte(1L, &timeout)) == EOF)
+	    break;
+	if (!endpos || next != endesc[endpos++])
+	    endpos = (next == *endesc);
+	if (imeta(next)) {
+	    pbuf[current++] = Meta;
+	    pbuf[current++] = next ^ 32;
+	} else if (next == '\r')
+	    pbuf[current++] = '\n';
+	else
+	    pbuf[current++] = next;
+    }
+    pbuf[current-endpos] = '\0';
+    return pbuf;
+}
+
+/**/
+int
+bracketedpaste(char **args)
+{
+    char *pbuf = bracketedstring();
+
+    if (*args) {
+	setsparam(*args, pbuf);
+    } else {
+	int n;
+	ZLE_STRING_T wpaste;
+	wpaste = stringaszleline((zmult == 1) ? pbuf :
+	    quotestring(pbuf, NULL, QT_BACKSLASH), 0, &n, NULL, NULL);
+	zmult = 1;
+	if (region_active)
+	    killregion(zlenoargs);
+	doinsert(wpaste, n);
+	free(pbuf); free(wpaste);
+    }
+    return 0;
+}
+
+/**/
 int
 overwritemode(UNUSED(char **args))
 {
@@ -1264,6 +1316,22 @@ executenamedcommand(char *prmt)
 	    if (listed)
 		clearlist = listshown = 1;
 	    curlist = 0;
+	} else if (cmd == Th(z_bracketedpaste)) {
+	    char *insert = bracketedstring();
+	    size_t inslen = strlen(insert);
+	    if (len + inslen > NAMLEN)
+		feep = 1;
+	    else {
+		strcpy(ptr, insert);
+		len += inslen;
+		ptr += inslen;
+		if (listed) {
+		    clearlist = listshown = 1;
+		    listed = 0;
+		} else
+		    curlist = 0;
+	    }
+	    free(insert);
 	} else {
 	    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
 		Thingy r;