summaryrefslogtreecommitdiff
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.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;