Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: use escape sequence for system clipboard
- X-seq: zsh-workers 50934
- From: Oliver Kiddle <opk@xxxxxxx>
- To: Zsh workers <zsh-workers@xxxxxxx>
- Subject: PATCH: use escape sequence for system clipboard
- Date: Fri, 11 Nov 2022 01:22:13 +0100
- Archived-at: <https://zsh.org/workers/50934>
- List-id: <zsh-workers.zsh.org>
Terminals increasingly support a feature for setting the system
clipboard with the OSC 52[1] escape sequence. Vim has "* and "+
registers corresponding to the primary selection and clipboard
respectively. In vim, these often work even in a terminal because the
vim binary has been linked against X libraries. In the past, I pondered
adding hooks that could be defined to use xclip/pbcopy/whatever but the
escape sequence has the advantage of working through ssh without X
forwarding or even over something like telnet or cu. tmux also supports
it. A notable limitation is that put/paste commands are not supported. I
guess most terminals already have a key combination like Shift-Insert
for inserting the X selection.
This patch makes zsh generate the sequence if you do something like "+yy
in vi mode. Terminals that lack support for the feature typically
silently swallow and discard the sequence. There may be old ones where
it will spew crap into the terminal but I don't see that as an issue
given the obscure keystroke needed. Paste operations will simply do
nothing.
I don't know if there is a suitable corresponding way this could be
exposed to emacs mode users? Note that it does appear to work fairly
well to define a widget that uses the vi widgets. But perhaps we could
make it easier?
x-copy() {
(( REGION_ACTIVE )) || zle beep
zle vi-set-buffer \*
zle vi-yank
}
zle -N x-copy
If you use, rxvt-unicode note that it needs a perl extension to enable
the feature.
Oliver
[1] See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 2d033a0a1..58700072a 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -2470,10 +2470,11 @@ command. tt(run-help) is normally aliased to tt(man).
tindex(vi-set-buffer)
item(tt(vi-set-buffer) (unbound) (tt(")) (unbound))(
Specify a buffer to be used in the following command.
-There are 37 buffers that can be specified:
+There are 39 buffers that can be specified:
the 26 `named' buffers tt("a) to tt("z), the `yank' buffer tt("0),
-the nine `queued' buffers tt("1) to tt("9) and the `black hole' buffer
-tt("_). The named buffers can also be specified as tt("A) to tt("Z).
+the nine `queued' buffers tt("1) to tt("9), the `black hole' buffer
+tt("_) and the system selection tt("*) and clipboard tt("+).
+The named buffers can also be specified as tt("A) to tt("Z).
When a buffer is specified for a cut, change or yank command, the text
concerned replaces the previous contents of the specified buffer. If
@@ -2482,6 +2483,10 @@ appended to the buffer instead of overwriting it. When using the tt("_)
buffer, nothing happens. This can be useful for deleting text without
affecting any buffers.
+Updating the system clipboard relies on specific support from the terminal.
+Reading it is not possible so a paste command with tt("*) or tt("+) will do
+nothing.
+
If no buffer is specified for a cut or change command, tt("1) is used, and
the contents of tt("1) to tt("8) are each shifted along one buffer;
the contents of tt("9) is lost. If no buffer is specified for a yank
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 391586c4a..f59545397 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -258,6 +258,9 @@ struct modifier {
#define MOD_NULL (1<<5) /* throw away text for the vi cut buffer */
#define MOD_CHAR (1<<6) /* force character-wise movement */
#define MOD_LINE (1<<7) /* force line-wise movement */
+#define MOD_PRI (1<<8) /* OS primary selection for the vi cut buffer */
+#define MOD_CLIP (1<<9) /* OS clipboard for the vi cut buffer */
+#define MOD_OSSEL (MOD_PRI | MOD_CLIP) /* either system selection */
/* current modifier status */
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 526216fa7..3d9017dcf 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -936,6 +936,28 @@ cut(int i, int ct, int flags)
cuttext(zleline + i, ct, flags);
}
+static char*
+base64_encode(const char *src, size_t len) {
+ static const char* base64_table =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ const unsigned char *end = (unsigned char *)src + len;
+ const unsigned char *in = (unsigned char *)src;
+ char *ret = zhalloc(1 + 4 * ((len + 2) / 3)); /* 4 bytes out for 3 in */
+ char *cur = ret;
+
+ for (; end - in > 0; in += 3, cur += 4) {
+ unsigned int n = *in << 16;
+ cur[3] = end - in > 2 ? base64_table[(n |= in[2]) & 0x3f] : '=';
+ cur[2] = end - in > 1 ? base64_table[((n |= in[1]<<8) >> 6) & 0x3f] : '=';
+ cur[1] = base64_table[(n >> 12) & 0x3f];
+ cur[0] = base64_table[n >> 18];
+ }
+ *cur = '\0';
+
+ return ret;
+}
+
/*
* As cut, but explicitly supply the text together with its length.
*/
@@ -948,7 +970,15 @@ cuttext(ZLE_STRING_T line, int ct, int flags)
return;
UNMETACHECK();
- if (zmod.flags & MOD_VIBUF) {
+ if (zmod.flags & MOD_OSSEL) {
+ int cutll;
+ char *mbcut = zlelineasstring(line, ct, 0, &cutll, NULL, 1);
+ unmetafy(mbcut, &cutll);
+ mbcut = base64_encode(mbcut, cutll);
+
+ fprintf(shout, "\e]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p',
+ mbcut);
+ } else if (zmod.flags & MOD_VIBUF) {
struct cutbuffer *b = &vibuf[zmod.vibuf];
if (!(zmod.flags & MOD_VIAPP) || !b->buf) {
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 0f198d0e8..c7fd0e27b 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -1014,6 +1014,9 @@ int
visetbuffer(char **args)
{
ZLE_INT_T ch;
+ ZLE_CHAR_T *match = ZWS("_*+");
+ int registermod[] = { MOD_NULL, MOD_PRI, MOD_CLIP };
+ ZLE_CHAR_T *found;
if (*args) {
ch = **args;
@@ -1022,12 +1025,13 @@ visetbuffer(char **args)
} else {
ch = getfullchar(0);
}
- if (ch == ZWC('_')) {
- zmod.flags |= MOD_NULL;
+ found = ZS_strchr(match, ch);
+ if (found) {
+ zmod.flags |= registermod[found - match];
prefixflag = 1;
return 0;
} else
- zmod.flags &= ~MOD_NULL;
+ zmod.flags &= ~(MOD_NULL | MOD_OSSEL);
if ((ch < ZWC('0') || ch > ZWC('9')) &&
(ch < ZWC('a') || ch > ZWC('z')) &&
(ch < ZWC('A') || ch > ZWC('Z')))
Messages sorted by:
Reverse Date,
Date,
Thread,
Author