Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: bracketed paste mode in xterm and urxvt



On 5 Jun, Yuri D'Elia wrote:
> What if the terminal itself turns off BREAK processing during paste?
You can try. Would be interesting. May not work when there is ssh/sshd,
telnet, screen, tmux etc sat between urxvt and zsh.

> Putty also eats it by the way. If that's the case for all terminals, I
> would just enabled it by default.

We can always add some tests later if it appears to be necessary. Any
objections?
I've tried quite hard to find a terminal that won't eat the sequence and
found none.

This links it to a new BRACKETED_PASTE option. A concern with that
approach is that if logic is needed in init_term() to disable the
feature, the distinction between the user explicitly disabling the
feature and init_term() doing it wouldn't be clear.

Any thoughts on the exact positioning of the code to print the
enable/disable sequences here? Is there any way the disable sequence
could get missed? Doesn't seem necessary to do anything in trashzle().
Unlike zle-line-finish (but like $POSTEDIT), this prints the disable
sequence even if zle is aborted with ^C or ^D. It is also important that
it comes before preexec is run because that is where you might enable
bracketed paste for specific commands.

This includes the previous patch. The region is now replaced if active
and the widget is also bound for vi command mode.

Oliver

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index fa54024..6b32d89 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -2291,6 +2291,20 @@ cindex(enabling the beep)
 item(tt(BEEP) (tt(PLUS()B)) <D>)(
 Beep on error in ZLE.
 )
+pindex(BRACKETED_PASTE)
+pindex(NO_BRACKETED_PASTE)
+pindex(NOBRACKETED_PASTE)
+cindex(bracketed paste)
+cindex(enabling bracketed paste)
+item(tt(BRACKETED_PASTE))(
+Many terminal emulators have a feature that allow applications to
+identify when text is pasted into the terminal rather than being typed
+normally. If this option is set, that feature will be enabled while ZLE
+is active and disabled at other times. In particular, this means that
+special characters such as tabs and newlines will 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.
+)
 pindex(COMBINING_CHARS)
 pindex(NO_COMBINING_CHARS)
 pindex(COMBININGCHARS)
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 16d661f..652f996 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -2057,6 +2057,11 @@ 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.
+See also the BRACKETED_PASTE option.
+)
 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/iwidgets.list b/Src/Zle/iwidgets.list
index b41661a..6a07212 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_keymap.c b/Src/Zle/zle_keymap.c
index c6fae25..d355f41 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 cec44c0..47b5aa5 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1248,6 +1248,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 
     zlecallhook(init, NULL);
 
+    if (isset(BRACKETEDPASTE))
+        fputs("\e[?2004h", 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 (isset(BRACKETEDPASTE))
+        fputs("\e[?2004l", shout);
+
     if (done && !exit_pending && !errflag)
 	zlecallhook(finish, NULL);
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4669ef2..873b01b 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -737,6 +737,44 @@ yankpop(UNUSED(char **args))
 
 /**/
 int
+bracketedpaste(UNUSED(char **args))
+{
+    static const char endesc[] = "\e[201~";
+    int endpos = 0;
+    size_t psize = 64;
+    char *buf, *pbuf = zalloc(psize);
+    size_t current = 0;
+    int n, next, timeout;
+    ZLE_STRING_T wpaste;
+
+    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';
+    buf = zmult == 1 ? pbuf : quotestring(pbuf, NULL, QT_BACKSLASH);
+    zmult = 1;
+    wpaste = stringaszleline(buf, 0, &n, NULL, NULL);
+    if (region_active)
+	killregion(zlenoargs);
+    doinsert(wpaste, n);
+    free(pbuf); free(wpaste);
+    return 0;
+}
+
+/**/
+int
 overwritemode(UNUSED(char **args))
 {
     insmode ^= 1;
diff --git a/Src/options.c b/Src/options.c
index 78f603d..440783f 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -100,6 +100,7 @@ static struct optname optns[] = {
 {{NULL, "beep",		      OPT_ALL},			 BEEP},
 {{NULL, "bgnice",	      OPT_EMULATE|OPT_NONBOURNE},BGNICE},
 {{NULL, "braceccl",	      OPT_EMULATE},		 BRACECCL},
+{{NULL, "bracketedpaste",     OPT_ALL},			 BRACKETEDPASTE},
 {{NULL, "bsdecho",	      OPT_EMULATE|OPT_SH},	 BSDECHO},
 {{NULL, "caseglob",	      OPT_ALL},			 CASEGLOB},
 {{NULL, "casematch",	      OPT_ALL},			 CASEMATCH},
diff --git a/Src/zsh.h b/Src/zsh.h
index c88c2e7..ff3793a 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2075,6 +2075,7 @@ enum {
     BEEP,
     BGNICE,
     BRACECCL,
+    BRACKETEDPASTE,
     BSDECHO,
     CASEGLOB,
     CASEMATCH,



Messages sorted by: Reverse Date, Date, Thread, Author