Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: terminal integration with semantic markers



Many of the newer terminals support OSC 133 for semantic markers so the
shell can advise them of where prompts, right prompts, input areas and
output are. This enables features such as jumping to prompts or
selecting the output from a single command. There's a specification
here: https://gitlab.freedesktop.org/Per_Bothner/specifications/blob/master/proposals/semantic-prompts.md

For this to work, users end up needing to install plugins. The patch
adds direct support which is hopefully somewhat easier.

This also brings bracketed-paste in as a value in .term.extensions
so that we have a consistent way to disable all terminal extension
features. And because there are a few sequences that need printing at
the start and end of input editing. The two modkeys extensions are also
examples of these but they remain disabled pending further work.

I've not previously gained much experience with using these sequences so
perhaps other people are aware of some peculiarities and workarounds.
For example, I'm not certain whether the start of input sequence needs
reprinting when the prompt gets redrawn in a widget.

The plugins typically use $$ for the application ID. Again, I'm not sure
how an terminals use this identifier. I mixed in a hash of the hostname
but it may be better to use a random number (fixed after initialisation
in a static variable).

I also tried v instead of m as the mouse mode specified in the sequence
but it doesn't seem to work either way for moving to later lines.

Also here is the use of OSC 7 for reporting the current directory which
allows the terminal to create new windows in the same working directory.

OSC 1337 also exists for setting user variables. These seem to be fairly
terminal specific, even having names such as WEZTERM_USER so I'm not
sure we can do much of any use with that.

Oliver

diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index f8a035b13..46ad9cfde 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1746,6 +1746,30 @@ startup.  Extensions can be any of the following, where the marks `<D>' and
 `<E>' indicate whether they are disabled or enabled by default:
 
 startitem()
+item(tt(bracketed-paste) <E>)(
+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.
+)
+item(tt(integration-output) <E>)(
+This provides the terminal with semantic information regarding where the output
+from commands start and finish. Some terminals use this information to make it
+easy to select just the output from a single command.
+)
+item(tt(integration-prompt) <E>)(
+This informs the terminal when the shell displays a prompt. This enables a
+variety of terminal features such as displaying markers, allowing you to jump
+to previous commands in the scroll-back buffer and ensuring that right prompts
+are handled correctly when the window is resized. The end of the prompt also
+provides the terminal with a marker for the start of user input.
+)
+item(tt(integration-pwd) <E>)(
+This advises the terminal of the shell's current directory which allows it
+to create new windows with the same current working directory.
+)
 item(tt(query-bg) <E>)(
 Query the terminal background color which is used for tt(.term.bg) and
 tt(.term.mode).
@@ -1764,6 +1788,11 @@ wider range of key combinations and resolves problems with ambiguous key
 sequences.  Currently there is only support for detecting whether the terminal
 supports this feature.
 )
+item(tt(modkeys-xterm) <D>)(
+Support for the keyboard handling sequences associated with xterm's
+tt(modifyOtherKeys) X resource. This enables reporting of a wider range of key
+combinations and resolves some problems with ambiguous key sequences.
+)
 xitem(tt(truecolor) <D>)
 item(tt(query-truecolor) <E>)(
 Support for 24-bit truecolor escape sequences.  Auto-detection also tries
@@ -1877,18 +1906,13 @@ 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.
+This two-element array contains the terminal escape sequences for enabling and
+disabling the bracketed paste feature which allows ZLE to discern text that is
+pasted into the terminal.  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.
+However, see also the tt(.term.extensions) parameter which provides a single
+place to enable or disable terminal features.
 )
 vindex(zle_highlight)
 item(tt(zle_highlight))(
diff --git a/Src/Zle/termquery.c b/Src/Zle/termquery.c
index c696c6147..e5840ba3d 100644
--- a/Src/Zle/termquery.c
+++ b/Src/Zle/termquery.c
@@ -230,8 +230,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 */
@@ -578,6 +578,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)
@@ -595,5 +617,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 5fe5cd62d..0f5c03562 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1216,9 +1216,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 */
@@ -1233,7 +1234,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);
@@ -1271,10 +1272,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();
@@ -1345,6 +1347,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 */
@@ -1354,8 +1360,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();
 
@@ -1366,8 +1371,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);
@@ -1896,11 +1900,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;
@@ -1998,6 +2004,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
@@ -2016,7 +2023,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;
@@ -2024,7 +2031,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);
@@ -2178,6 +2185,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);
diff --git a/Src/builtin.c b/Src/builtin.c
index 5563bdba9..2bb6306c5 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1258,8 +1258,11 @@ cd_new_pwd(int func, LinkNode dir, int quiet)
     /* execute the chpwd function */
     fflush(stdout);
     fflush(stderr);
-    if (!quiet)
+    if (!quiet) {
 	callhookfunc("chpwd", NULL, 1, NULL);
+	if (zle_load_state == 1)
+	    zleentry(ZLE_CMD_CHPWD);
+    }
 
     dirstacksize = getiparam("DIRSTACKSIZE");
     /* handle directory stack sizes out of range */
@@ -4802,7 +4805,7 @@ bin_print(char *name, char **args, Options ops, int func)
 	     */
 	    char *str = unmetafy(
 		promptexpand(metafy(args[n], len[n], META_NOALLOC),
-			     0, NULL, NULL),
+			     0, NULL, NULL, NULL),
 		&len[n]);
 	    args[n] = dupstrpfx(str, len[n]);
 	    free(str);
diff --git a/Src/init.c b/Src/init.c
index 76de0b449..20b4ab735 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -211,12 +211,17 @@ loop(int toplevel, int justonce)
 		 */
 		errflag &= ~ERRFLAG_ERROR;
 	    }
+	    if (toplevel && zle_load_state == 1)
+		zleentry(ZLE_CMD_PREEXEC);
 	    if (stopmsg)	/* unset 'you have stopped jobs' flag */
 		stopmsg--;
 	    execode(prog, 0, 0, toplevel ? "toplevel" : "file");
 	    tok = toksav;
-	    if (toplevel)
+	    if (toplevel) {
 		noexitct = 0;
+		if (zle_load_state == 1)
+		    zleentry(ZLE_CMD_POSTEXEC);
+	    }
 	}
 	if (ferror(stderr)) {
 	    zerr("write error");
@@ -1801,7 +1806,7 @@ VA_DCL
 
 	lp = va_arg(ap, char **);
 
-	pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL),
+	pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, NULL),
 			  &pptlen);
 	write_loop(2, pptbuf, pptlen);
 	free(pptbuf);
diff --git a/Src/input.c b/Src/input.c
index 320fd6500..2bb333852 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -399,7 +399,7 @@ inputline(void)
 	    char *pptbuf;
 	    int pptlen;
 	    pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL,
-					   0, NULL, NULL), &pptlen);
+		    0, NULL, NULL, NULL), &pptlen);
 	    write_loop(2, pptbuf, pptlen);
 	    free(pptbuf);
 	}
diff --git a/Src/loop.c b/Src/loop.c
index 979285abc..4cd702aba 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -282,7 +282,7 @@ execselect(Estate state, UNUSED(int do_exec))
 		    /* Keep any user interrupt error status */
 		    errflag = oef | (errflag & ERRFLAG_INT);
 	    	} else {
-		    str = promptexpand(prompt3, 0, NULL, NULL);
+		    str = promptexpand(prompt3, 0, NULL, NULL, NULL);
 		    zputs(str, stderr);
 		    free(str);
 		    fflush(stderr);
diff --git a/Src/prompt.c b/Src/prompt.c
index 762d25650..25392b4c2 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -174,7 +174,7 @@ promptpath(char *p, int npath, int tilde)
 
 /**/
 mod_export char *
-promptexpand(char *s, int ns, char *rs, char *Rs)
+promptexpand(char *s, int ns, const char *marker, char *rs, char *Rs)
 {
     struct buf_vars new_vars;
 
@@ -218,6 +218,11 @@ promptexpand(char *s, int ns, char *rs, char *Rs)
     new_vars.bp1 = NULL;
     new_vars.truncwidth = 0;
 
+    if (marker && *s) {
+	*new_vars.bp++ = Inpar;
+	strucpy(&new_vars.bp, (char *) marker);
+	*new_vars.bp++ = Outpar;
+    }
     putpromptchar(1, '\0');
     addbufspc(2);
     if (new_vars.dontcount)
@@ -321,7 +326,7 @@ parsecolorchar(zattr arg, int is_fg)
 	    *ep = '\0';
 	    /* expand the contents of the argument so you can use
 	     * %v for example */
-	    coll = col = promptexpand(bv->fm, 0, NULL, NULL);
+	    coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL);
 	    *ep = oc;
 	    arg = match_colour((const char **)&coll, is_fg, 0);
 	    free(col);
diff --git a/Src/subst.c b/Src/subst.c
index a079672df..10c0913de 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -4000,7 +4000,7 @@ colonsubscript:
 		char *tmps;
 		untokenize(*ap);
 		txtunknownattrs = TXT_ATTR_ALL;
-		tmps = promptexpand(*ap, 0, NULL, NULL);
+		tmps = promptexpand(*ap, 0, NULL, NULL, NULL);
 		*ap = dupstring(tmps);
 		free(tmps);
 	    }
@@ -4010,7 +4010,7 @@ colonsubscript:
 		val = dupstring(val), copied = 1;
 	    untokenize(val);
 	    txtunknownattrs = TXT_ATTR_ALL;
-	    tmps = promptexpand(val, 0, NULL, NULL);
+	    tmps = promptexpand(val, 0, NULL, NULL, NULL);
 	    val = dupstring(tmps);
 	    free(tmps);
 	}
diff --git a/Src/utils.c b/Src/utils.c
index 19fd61a8b..a00949bd0 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -1544,7 +1544,7 @@ preprompt(void)
 	    eolmark = "%B%S%#%s%b";
 	opts[PROMPTPERCENT] = 1;
 	txtunknownattrs = TXT_ATTR_ALL;
-	str = promptexpand(eolmark, 1, NULL, NULL);
+	str = promptexpand(eolmark, 1, NULL, NULL, NULL);
 	countprompt(str, &w, 0, -1);
 	opts[PROMPTPERCENT] = percents;
 	zputs(str, shout);
@@ -1714,7 +1714,7 @@ printprompt4(void)
 	opts[XTRACE] = 0;
 	unmetafy(s, &l);
 	s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC),
-				  0, NULL, NULL), &l);
+				  0, NULL, NULL, NULL), &l);
 	opts[XTRACE] = t;
 
 	fprintf(xtrerr, "%s", s);
@@ -3263,7 +3263,7 @@ spckword(char **s, int hist, int cmd, int ask)
 		x = 'n';
 	    } else if (shout) {
 		char *pptbuf;
-		pptbuf = promptexpand(sprompt, 0, best, guess);
+		pptbuf = promptexpand(sprompt, 0, NULL, best, guess);
 		zputs(pptbuf, shout);
 		free(pptbuf);
 		fflush(shout);
diff --git a/Src/zsh.h b/Src/zsh.h
index 4e5c02980..53727f861 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -3235,7 +3235,10 @@ enum {
     ZLE_CMD_REFRESH,
     ZLE_CMD_SET_KEYMAP,
     ZLE_CMD_GET_KEY,
-    ZLE_CMD_SET_HIST_LINE
+    ZLE_CMD_SET_HIST_LINE,
+    ZLE_CMD_PREEXEC,
+    ZLE_CMD_POSTEXEC,
+    ZLE_CMD_CHPWD
 };
 
 /***************************************/
diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst
index be0564912..ecb309605 100644
--- a/Test/X04zlehighlight.ztst
+++ b/Test/X04zlehighlight.ztst
@@ -12,7 +12,7 @@
       zpty zsh "${(q)ZTST_testdir}/../Src/zsh -fiV +Z"
       zpty -w zsh "module_path=( ${(j< >)${(@q-)module_path}} \$module_path )"
       zpty -w zsh 'zle_highlight=( fg_start_code:"CDE|3" fg_end_code:"|" bg_start_code:"BCDE|4" bg_end_code:"|" )'
-      zpty -w zsh '.term.extensions=( -query truecolor )'
+      zpty -w zsh '.term.extensions=( -query truecolor -bracketed-paste -integration )'
     }
     zpty_input() {
       zpty ${${(M)2:#nonl}:+-n} -w zsh "$1"
@@ -20,7 +20,7 @@
     zpty_enable_zle() {
       zpty -w zsh "tcfunc() { REPLY=""; }"
       # This line will not be echoed back, behaving like ! -o zle
-      zpty -w zsh "setopt zle; zle -T tc tcfunc; unset zle_bracketed_paste"
+      zpty -w zsh "setopt zle; zle -T tc tcfunc"
     }
     zpty_line() {
       setopt localoptions extendedglob noshwordsplit
diff --git a/Test/X06termquery.ztst b/Test/X06termquery.ztst
index cce4e626f..5f3a81aae 100644
--- a/Test/X06termquery.ztst
+++ b/Test/X06termquery.ztst
@@ -9,6 +9,7 @@
       zpty -d
       zpty zsh "${(q)ZTST_testdir}/../Src/zsh -fiV +Z"
       zpty -w zsh "module_path=( ${(j< >)${(@q-)module_path}} \$module_path )"
+      zpty -w zsh ".term.extensions=( -bracketed-paste -integration )"
       zpty -w zsh "setopt zle"
       zpty -r zsh REPLY $'\e*\r'
       zpty -n -w zsh "$1"
@@ -30,7 +31,7 @@
 >typeset .term.bg='#ffffdd'
 >typeset .term.version=1.20.2
 >typeset .term.mode=light
->typeset -a .term.extensions=( modkeys-kitty truecolor )
+>typeset -a .term.extensions=( -bracketed-paste -integration modkeys-kitty truecolor )
 
   termresp $'\e]11;rgb:0/0/0\e\\\e]10;rgb:ff/ff/ff\e\\\eP>|Wayst(0.0.0)\e\\\e[?63;1;4c'
 0:wayst response to terminal queries (shorter colour sequences)
@@ -39,6 +40,7 @@
 >typeset .term.bg='#000000'
 >typeset .term.version=0.0.0
 >typeset .term.mode=dark
+>typeset -a .term.extensions=( -bracketed-paste -integration )
 
   termresp $'\e]11;rgb:0000/0000/0000\e\\\e]10;rgb:b2b2/b2b2/b2b2\e\\\eP1+r524742=382F382F38\e\\\eP>|WezTerm 20240203-110809-5046fc22\e\\\e[?65;4;6;18;22c'
 0:WezTerm response to terminal queries (space separates version and longer RGB response)
@@ -47,13 +49,14 @@
 >typeset .term.bg='#000000'
 >typeset .term.version=20240203-110809-5046fc22
 >typeset .term.mode=dark
->typeset -a .term.extensions=( truecolor )
+>typeset -a .term.extensions=( -bracketed-paste -integration truecolor )
 
   termresp $'\e]11;rgb:9600/8700/7900\e\e]10;rgb:0000/0000/0000\e\e[?1;2c'
 0:urxvt response to terminal queries (bug in end of colour sequences)
 >typeset .term.fg='#000000'
 >typeset .term.bg='#968779'
 >typeset .term.mode=light
+>typeset -a .term.extensions=( -bracketed-paste -integration )
 
   termresp $'\e]11;rgb:0000/0000/0000\e\\\e]10;rgb:dddd/dddd/dddd\e\\\e[?0u\eP0+r524742\e\\\eP>|kitty(0.36.4)\e\\\e[?62;c'
 0:kitty response to terminal queries (responds with error to RGB request)
@@ -62,7 +65,7 @@
 >typeset .term.bg='#000000'
 >typeset .term.version=0.36.4
 >typeset .term.mode=dark
->typeset -a .term.extensions=( modkeys-kitty )
+>typeset -a .term.extensions=( -bracketed-paste -integration modkeys-kitty )
 
   termresp $'\e]11;rgb:0000/ffff/8c8c\e\\\e]10;rgb:0000/0000/0000\e\\\eP1+r524742=38\e\\\eP>|XTerm(396)\e\\\e[?64;1;2;6;9;15;16;17;18;21;22;28c'
 0:xterm response to terminal queries
@@ -71,7 +74,7 @@
 >typeset .term.bg='#00ff8c'
 >typeset .term.version=396
 >typeset .term.mode=light
->typeset -a .term.extensions=( truecolor )
+>typeset -a .term.extensions=( -bracketed-paste -integration truecolor )
 
   termresp $'echo type\e]11;rgb:0/0/0\aah\e]10;rgb:A/B/C\aea\e[?0u\eP0+r\e\\d\n\e[?0;c'
 0:type-ahead
@@ -79,24 +82,28 @@
 >typeset .term.fg='#0a0b0c'
 >typeset .term.bg='#000000'
 >typeset .term.mode=dark
->typeset -a .term.extensions=( modkeys-kitty )
+>typeset -a .term.extensions=( -bracketed-paste -integration modkeys-kitty )
 
   termresp ''
 0:no response - timeout
+>typeset -a .term.extensions=( -bracketed-paste -integration )
 
 # Following three vi-put tests also cover 0, 1 and 2 `=' padding
 # characters in the base64 decoding.
   termresp $'\e[?0;cbindkey -v\necho \e"*p\e]52;p;YWZ0ZXI=\aa\n'
 0:paste after from clipboard
 >after
+>typeset -a .term.extensions=( -bracketed-paste -integration )
 
   termresp $'\e[?0;cbindkey -v\necho X\e"*P\e]52;p;YmVmb3Jl\aa\n'
 0:paste before from clipboard
 >beforeX
+>typeset -a .term.extensions=( -bracketed-paste -integration )
 
   termresp $'\e[?0;cbindkey -v\necho X\ev"*p\e]52;p;cmVwbGFjZQ==\aa\n'
 0:paste over from clipboard
 >replace
+>typeset -a .term.extensions=( -bracketed-paste -integration )
 
 %clean
 




Messages sorted by: Reverse Date, Date, Thread, Author