From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23867 invoked by alias); 15 Jun 2015 22:17:48 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 35474 Received: (qmail 6786 invoked from network); 15 Jun 2015 22:17:45 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2 autolearn=ham autolearn_force=no version=3.4.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.co.uk; s=s2048; t=1434406319; bh=5f15pq16pwCvHfmPPiu8KzxY3CiopGLMhTBHSXEltZg=; h=In-reply-to:From:References:To:Subject:Date:From:Subject; b=uePqMQHS6n8qanML0SCwvp5tS9zK05/4ulknIHjRERNkGBlmVeCxtpGdWj+zc8UyXqV68kKzw4MPmUX/iAsbuhK/rLqbtRPDaWdrlAj7AMK3/EMcvctnYye7+2zjFJ84t3pFSxtVXrae6q+KH/v3VXPQ4gWVwWj4VD/nJVt6GVd/9egz17HQwPesDO3LnXuRfu86OrC43MxQ2qLqhShnI+F4Z6zwZ+/4tuXU6Kh9HPAwVfV/2/3hVg8JbjVf0b86vp+U9mMrm0ouQwzjIwSohSHW/UNeZ9EEv0EWTDYSbDm7S4DbYPCF9qKxbMMBHgwuxQYev1T+eWeUIuCsT6ANfw== X-Yahoo-Newman-Id: 675002.37298.bm@smtp107.mail.ir2.yahoo.com X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: 9Apf7MoVM1nTNccer9BZJHypu_a8pVO5xTRHIHib7SmyvPx EsvxJqBd4Ba1TLMQjTfAsbA1Nk5FM2eV9avPX_NfkFVprJXwkpZ__DNnCD1s DPMY9AMC__M2mighW5RMghH_qlmsPUnagTsRlJZTGwMLVYKJ3GRXxEjm3JXW 3p8NYUACAVp6fbwzv0PBVO2yxkB9phXyi0QjyDbM9JnL90uQoNGotL9xsJMA zcwj0tzx7o1VYF_WC96ZYhM6qajzDzS0YNVRSuicg46vht60HPVgmDFGupP2 IneWkcr67N1_72WaoCsOhRtTHe5m3QQlWvl2LzotCjfqFNltkS57xGn2kiTK 8rJOLGXoD8dN..Be3zpy0uebw1j7Sua0niNHaRwS.uESx3BHiADfuJUmgqEo G4w4km_X2qydSvYcJIcSYKCL4wfFWqHlVJL7vk7wKTU6sAQBb2vGxpXocYYN o4uf_eboJRtEiOBLejkcZ2V4Xf8Wm5s8HipTtGfWFUc6McMkCCANlPsXMQ3. nAx8ZDWfWE8mJU1ocOBrIwV5RuLJrqxob X-Yahoo-SMTP: opAkk_CswBAce_kJ3nIPlH80cJI- In-reply-to: <150609213830.ZM29868@torch.brasslantern.com> From: Oliver Kiddle References: <55677AF5.50709@thequod.de> <27004.1433345491@thecus.kiddle.eu> <3098.1433511607@thecus.kiddle.eu> <7701.1433896116@thecus.kiddle.eu> <150609213830.ZM29868@torch.brasslantern.com> To: zsh-workers@zsh.org Subject: Re: bracketed paste mode in xterm and urxvt MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <20633.1434406318.1@thecus.kiddle.eu> Date: Tue, 16 Jun 2015 00:11:58 +0200 Message-ID: <20634.1434406318@thecus.kiddle.eu> On 9 Jun, Bart wrote: > This sounds like the same dilemma that we had with ZLE_RPROMPT_INDENT, > which we addressed with a variable rather than an option. This would > address several problems: namespace (ZLE-specific stuff should really > stop leaking into the base shell options); a place to store the string > to use in case it turns out not to be the same for all terminal types; > and distinguishing among unset/set/empty/non-empty semantics. That makes sense, thanks. So this next attempt uses ZLE_BRACKETED_PASTE_ON/OFF though I'm open to better ideas. I've not made them special, just initialised them with a call to setsparam as that seemed simple enough for the purpose. Is that reasonable or should they be special? I also looked around for places where keys are read and handled such that bracketedpaste() would not be called. I've not done anything in the case of single bytes such as for the vi r and f commands but I have added handling for the history and completion incremental searches and for the execute-named-command widget. It would be possible to avoid this special handling by adding logic to getkeycmd() so that they get a stream of self-insert widgets. That would somehow defeat the purpose of bracketed paste, however. In both the incremental search modes, backspace works like undo so the entire paste is undone. This same effect is also apparent if you press backspace after changing search direction with Ctrl-S or Ctrl-R. Oliver diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index eb3eb36..48c973a 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1642,6 +1642,25 @@ item(tt(ZDOTDIR))( The directory to search for shell startup files (.zshrc, etc), if not tt($HOME). ) +vindex(ZLE_BRACKETED_PASTE_ON) +vindex(ZLE_BRACKETED_PASTE_OFF) +cindex(bracketed paste) +cindex(enabling bracketed paste) +xitem(tt(ZLE_BRACKETED_PASTE_ON)) +item(tt(ZLE_BRACKETED_PASTE_OFF))( +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. + +These parameters contain 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 parameters 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 16d661f..9066681 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 ZLE_BRACKETED_PASTE_ON 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 f542066..a55567f 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; @@ -3294,17 +3278,43 @@ domenuselect(Hookdef dummy, Chdata dat) } else { msearchstr = ""; 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 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_hist.c b/Src/Zle/zle_hist.c index cc66f99..f8aab20 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 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..62ed157 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 = getsparam("ZLE_BRACKETED_PASTE_ON"))) + 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 = getsparam("ZLE_BRACKETED_PASTE_OFF"))) + fputs(bracket, shout); + if (done && !exit_pending && !errflag) zlecallhook(finish, NULL); @@ -2028,6 +2034,9 @@ setup_(UNUSED(Module m)) clwords = (char **) zshcalloc((clwsize = 16) * sizeof(char *)); + setsparam("ZLE_BRACKETED_PASTE_ON", ztrdup("\033[?2004h")); + setsparam("ZLE_BRACKETED_PASTE_OFF", ztrdup("\033[?2004l")); + return 0; } diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index 4669ef2..ef9d0a8 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -736,6 +736,54 @@ 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(UNUSED(char **args)) +{ + int n; + ZLE_STRING_T wpaste; + char *pbuf = bracketedstring(); + + 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 +1312,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;