diff options
| author | Oliver Kiddle <okiddle@yahoo.co.uk> | 2025-11-10 21:02:31 +0100 |
|---|---|---|
| committer | Oliver Kiddle <opk@zsh.org> | 2025-11-10 21:02:31 +0100 |
| commit | 6a691a3487cc26900475291ced12400318176aeb (patch) | |
| tree | ba5f16395098b632fe16369e684eaaf18e05faed /Src/Zle | |
| parent | 53379, 53380: autoload nearcolor based on truecolor detection (diff) | |
| download | zsh-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.c | 170 | ||||
| -rw-r--r-- | Src/Zle/zle_main.c | 39 |
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); |
