summaryrefslogtreecommitdiffstats
path: root/Src/Zle
diff options
context:
space:
mode:
authorOliver Kiddle <okiddle@yahoo.co.uk>2025-11-10 21:02:31 +0100
committerOliver Kiddle <opk@zsh.org>2025-11-10 21:02:31 +0100
commit6a691a3487cc26900475291ced12400318176aeb (patch)
treeba5f16395098b632fe16369e684eaaf18e05faed /Src/Zle
parent53379, 53380: autoload nearcolor based on truecolor detection (diff)
downloadzsh-6a691a3487cc26900475291ced12400318176aeb.tar
zsh-6a691a3487cc26900475291ced12400318176aeb.tar.gz
zsh-6a691a3487cc26900475291ced12400318176aeb.tar.bz2
zsh-6a691a3487cc26900475291ced12400318176aeb.tar.lz
zsh-6a691a3487cc26900475291ced12400318176aeb.tar.xz
zsh-6a691a3487cc26900475291ced12400318176aeb.tar.zst
zsh-6a691a3487cc26900475291ced12400318176aeb.zip
53404: terminal integration with semantic markers
Diffstat (limited to 'Src/Zle')
-rw-r--r--Src/Zle/termquery.c170
-rw-r--r--Src/Zle/zle_main.c39
2 files changed, 196 insertions, 13 deletions
diff --git a/Src/Zle/termquery.c b/Src/Zle/termquery.c
index 7305ba1d1..c9fd22588 100644
--- a/Src/Zle/termquery.c
+++ b/Src/Zle/termquery.c
@@ -223,8 +223,8 @@ probe_terminal(const char *tquery, seqstate_t *states,
#endif
settyinfo(&ti);
- fputs(tquery, shout);
- fflush(shout);
+ write_loop(SHTTY, tquery, strlen(tquery));
+ notify_pwd(); /* unrelated to the function's main purpose */
while (!finish && *curstate) {
int consumed = 0; /* whether an input token has been matched */
@@ -571,6 +571,28 @@ handle_paste(UNUSED(int sequence), UNUSED(int *numbers), UNUSED(int len),
*(char**) output = base64_decode(capture, clen);
}
+static char*
+url_encode(const char* path, size_t *ulen)
+{
+ char *url = zhalloc(strlen(path) * 3 + 1); /* worst case length triples */
+ const char *in = path;
+ char *out = url;
+
+ for (; *in; in++) {
+ /* In theory, space can be encoded as '+' but not all terminals
+ * handled that and %20 works reliably.
+ * ':' as a Windows drive letter should also not be encoded */
+ if (isalnum(*in) || strchr("-._~/", *in))
+ *out++ = *in; /* untouched in encoding */
+ else
+ out += sprintf(out, "%%%02X", *in); /* otherwise %HH */
+ }
+
+ *out = '\0';
+ *ulen = out - url;
+ return url;
+}
+
/**/
char *
system_clipget(char clip)
@@ -588,5 +610,147 @@ void
system_clipput(char clip, char *content, size_t clen)
{
char *encoded = base64_encode(content, clen);
- fprintf(shout, "\033]52;%c;%s\a", clip, encoded);
+ fprintf(shout, "\033]52;%c;%s\033\\", clip, encoded);
+}
+
+/**/
+static int
+extension_enabled(const char *class, const char *ext, unsigned clen, int def)
+{
+ char **e, **elist = getaparam(EXTVAR);
+
+ for (e = elist; e && *e; e++) {
+ int negate = (**e == '-');
+ if (strncmp(*e + negate, class, clen))
+ continue;
+
+ if (!*(*e + negate + clen) || !strcmp(*e + negate + clen, ext))
+ return !negate;
+ }
+ return def;
+}
+
+
+struct extension {
+ char *key, *seq[2];
+ int class, enabled;
+};
+
+static const struct extension editext[] = {
+ { "bracketed-paste", { NULL, NULL}, 0, 1 },
+ { "integration-prompt", { "\033]133;B\033\\" }, 11, 1 },
+#if 0
+ { "modkeys-kitty", { "\033[=5u", "\033[=0u" }, 7, 0 },
+#endif
+ { "modkeys-xterm", { "\033[>4;1m", "\033[>4m" }, 7, 0 }
+};
+
+static void
+collate_seq(int sindex, int dir)
+{
+ char seq[256];
+ char *pos = seq;
+ int max = sizeof(editext) / sizeof(*editext);
+ int i;
+ char **bracket;
+ char **e, **elist = getaparam(EXTVAR);
+
+ for (i = dir > 0 ? 0 : max - 1; i >= 0 && i < max; i += dir) {
+ int enabled = editext[i].enabled;
+ if (i && !editext[i].seq[sindex])
+ continue;
+ for (e = elist; e && *e; e++) {
+ int negate = (**e == '-');
+ if (negate != enabled)
+ continue;
+ if ((editext[i].class &&
+ !strncmp(*e + negate, editext[i].key, editext[i].class) &&
+ !*(*e + negate + editext[i].class)) ||
+ !strcmp(*e + negate + editext[i].class,
+ editext[i].key + editext[i].class))
+ {
+ enabled = !negate;
+ break;
+ }
+
+ }
+ if (enabled) {
+ if (i)
+ strucpy(&pos, editext[i].seq[sindex]);
+ else if ((bracket = getaparam("zle_bracketed_paste")) &&
+ arrlen(bracket) == 2)
+ strucpy(&pos, bracket[sindex]);
+ }
+ }
+ write_loop(SHTTY, seq, pos - seq);
+}
+
+/**/
+void
+start_edit(void)
+{
+ collate_seq(0, 1);
+}
+
+/**/
+void
+end_edit(void)
+{
+ collate_seq(1, -1);
+}
+
+/**/
+const char **
+prompt_markers(void)
+{
+ static unsigned aid = 0;
+ static char pre[] = "\033]133;A;cl=m;aid=zZZZZZZ\033\\"; /* before the prompt */
+ static const char *const PR = "\033]133;P;k=i\033\\"; /* primary (PS1) */
+ static const char *const SE = "\033]133;P;k=s\033\\"; /* secondary (PS2) */
+ static const char *const RI = "\033]133;P;k=r\033\\"; /* right (RPS1,2) */
+ static const char *markers[] = { pre, PR, SE, RI };
+ static const char *nomark[] = { NULL, NULL, NULL, NULL };
+
+ if (!extension_enabled("integration", "prompt", 11, 1))
+ return nomark;
+
+ if (!aid) {
+ /* hostname and pid should uniquely identify a shell instance */
+ char *h = getsparam("HOST");
+ aid = (h ? hasher(h) : 0) ^ getpid();
+ if (!aid) aid = 1; /* unlikely but just to be safe */
+ /* base64 not required but it is safe, convenient and compact */
+ h = base64_encode((const char *)&aid, sizeof(aid));
+ memcpy(pre + 13, h, 6);
+ }
+
+ return markers;
+}
+
+/**/
+void
+mark_output(int start)
+{
+ static const char START[] = "\033]133;C\033\\";
+ static const char END[] = "\033]133;D\033\\";
+ if (extension_enabled("integration", "output", 11, 1))
+ write_loop(SHTTY, start ? START : END,
+ (start ? sizeof(START) : sizeof(END)) - 1);
+}
+
+/**/
+void
+notify_pwd(void)
+{
+ char *url;
+ size_t ulen;
+
+ if (!extension_enabled("integration", "pwd", 11, 1))
+ return;
+
+ url = url_encode(pwd, &ulen);
+ /* only "localhost" seems to be much use here as the host */
+ write_loop(SHTTY, "\033]7;file://localhost", 20);
+ write_loop(SHTTY, url, ulen);
+ write_loop(SHTTY, "\033\\", 2);
}
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 99ee3c804..6cea57112 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1215,9 +1215,10 @@ zlecore(void)
char *
zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
{
- char *s, **bracket;
+ char *s;
int old_errno = errno;
int tmout = getiparam("TMOUT");
+ const char **markers = prompt_markers();
#if defined(HAVE_POLL) || defined(HAVE_SELECT)
/* may not be set, but that's OK since getiparam() returns 0 == off */
@@ -1232,7 +1233,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
char *pptbuf;
int pptlen;
- pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL),
+ pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, NULL),
&pptlen);
pmpt_attr = txtcurrentattrs;
write_loop(2, pptbuf, pptlen);
@@ -1270,10 +1271,11 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
trashedzle = 0;
raw_lp = lp;
txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
- lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL);
+ lpromptbuf = promptexpand(lp ? *lp : NULL, 1,
+ markers[flags == ZLCON_LINE_CONT ? 2 : 1], NULL, NULL);
pmpt_attr = txtcurrentattrs;
raw_rp = rp;
- rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL);
+ rpromptbuf = promptexpand(rp ? *rp : NULL, 1, markers[2], NULL, NULL);
rpmpt_attr = txtcurrentattrs;
prompt_attr = mixattrs(pmpt_attr, rpmpt_attr);
free_prepostdisplay();
@@ -1344,6 +1346,10 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
prefixflag = 0;
region_active = 0;
+ /* semantic prompt marker printed before first prompt */
+ if (*markers)
+ write_loop(2, *markers, strlen(*markers));
+
zrefresh();
unqueue_signals(); /* Should now be safe to acknowledge SIGWINCH */
@@ -1353,8 +1359,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
if (zleline && *zleline)
redrawhook();
- if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
- fputs(*bracket, shout);
+ start_edit();
zrefresh();
@@ -1365,8 +1370,7 @@ 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);
+ end_edit();
if (done && !exit_pending && !errflag)
zlecallhook(finish, NULL);
@@ -1895,11 +1899,13 @@ describekeybriefly(UNUSED(char **args))
return 1;
clearlist = 1;
statusline = "Describe key briefly: _";
+ start_edit();
zrefresh();
if (invicmdmode() && region_active && (km = openkeymap("visual")))
selectlocalmap(km);
seq = getkeymapcmd(curkeymap, &func, &str);
selectlocalmap(NULL);
+ end_edit();
statusline = NULL;
if(!*seq)
return 1;
@@ -1997,6 +2003,7 @@ reexpandprompt(void)
static int looping;
if (!reexpanding++) {
+ const char **markers = prompt_markers();
/*
* If we're displaying a status in the prompt, it
* needs to be the toplevel one, not the one from
@@ -2015,7 +2022,7 @@ reexpandprompt(void)
looping = reexpanding;
txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0;
- new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL);
+ new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, markers[0], NULL, NULL);
pmpt_attr = txtcurrentattrs;
free(lpromptbuf);
lpromptbuf = new_lprompt;
@@ -2023,7 +2030,7 @@ reexpandprompt(void)
if (looping != reexpanding)
continue;
- new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL);
+ new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, markers[2], NULL, NULL);
rpmpt_attr = txtcurrentattrs;
prompt_attr = mixattrs(pmpt_attr, rpmpt_attr);
free(rpromptbuf);
@@ -2177,6 +2184,18 @@ zle_main_entry(int cmd, va_list ap)
break;
}
+ case ZLE_CMD_PREEXEC:
+ mark_output(1);
+ break;
+
+ case ZLE_CMD_POSTEXEC:
+ mark_output(0);
+ break;
+
+ case ZLE_CMD_CHPWD:
+ notify_pwd();
+ break;
+
default:
#ifdef DEBUG
dputs("Bad command %d in zle_main_entry", cmd);