Re: PATCH: fix complist interactive mode overwriting buffer

Mikael Magnusson <[email protected]>
Newsgroups gmane.comp.shells.zsh.devel
Message-ID <CAHYJk3RMxsN6uNHSDEfKX3W5JaEY5X1BQT6PJ=w8gaBGTCYO0Q@mail.gmail.com>
On Sun, May 24, 2026 at 1:49 AM Bart Schaefer <[email protected]> wrote:
>
> On Sat, May 23, 2026 at 4:26 PM Mikael Magnusson <[email protected]> wrote:
> >
> > If someone who uses interactive menu selection a lot feels like
> > testing these out, that would be helpful.
>
> Thanks for figuring this one out, it's been on my unresolved list for
> a long time.
>
> I've directly Cc'd Andrea and Marlon who each reported this problem at
> different times.

Thanks, I meant to do that but then it slipped my mind when I was
sending the patches out together.

Dana was talking about completing quoted words and I realized I hadn't
tried that at all, and it indeed doesn't work well very well.

First we have the interactive menu selection case like this:
% ls "<enter IMS, type NE and hit enter>
% ls "NEWS" <cursor here>E

    During completion, the quote tokens are temporarily removed, and we is
    decremented (this all happens in get_comp_string, around the parts
    involving skipchars, I would advise not looking too closely). Then the
    complist code restores the original string via setmline but forgot to
    restore we. I don't think there's any point in trying to track exactly
    how many characters were deleted to add them back, just set it back to
    the cursor position.
---
 Src/Zle/complist.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index f4b4df6c21..f4d22a968e 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -2779,9 +2779,13 @@ domenuselect(Hookdef dummy, Chdata dat)
            metafy_line();
            iforcemenu = 0;

-            if (cmd != Th(z_acceptandinfernexthistory))
+            if (cmd != Th(z_acceptandinfernexthistory)) {
                 modeline = setmstatus(status, saveline, savell, savecs,
                                       &modecs, &modell, &modelen);
+               /* if we are completing a quoted word, by this point we've
+                * lost track of this, so put we back where it should be */
+               we = savecs;
+           }

            if (nmatches < 1 || !minfo.cur || !*(minfo.cur)) {
                nolist = 1;


And secondly, this bug doesn't even involve the interactive mode.
% ls "stam<enter regular menu selection, hit alt-a (more than once if you want)>
% ls "stamp.h" stamp.h" stamp.h" stamp.h"

    Fix accept-and-hold completion for words with leading quotes

    The intention here is to skip leading braces probably, but that is
    a no-op because m->qipl is 0 in that case. When there is a quote, we
    don't want to skip it, so just remove that thing.
---
 Src/Zle/compresult.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index e3aa679ab2..3a73a2dd9f 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -588,9 +588,7 @@ instmatch(Cmatch m, int *scs)

     /* Ignored prefix. */
     if (m->ipre) {
-       char *p = m->ipre + (menuacc ? m->qipl : 0);
-
-       inststrlen(p, 1, (l = strlen(p)));
+       inststrlen(m->ipre, 1, (l = strlen(m->ipre)));
        r += l;
     }
     /* -P prefix. */


This is probably becoming a pain to try to apply manually, so I'll
attach the combined patch for anyone looking to try it out, although
at this point maybe I should just push?

-- 
Mikael Magnusson
ims.patch (text/x-patch, 2.6 KB)
diff --git c/Src/Zle/complist.c w/Src/Zle/complist.c
index 6a46497954..f4d22a968e 100644
--- c/Src/Zle/complist.c
+++ w/Src/Zle/complist.c
@@ -2640,6 +2640,7 @@ domenuselect(Hookdef dummy, Chdata dat)
     	}
 	do_last_key = 0;
 
+	int was_inter = (mode == MM_INTER);
 	if (!cmd || cmd == Th(z_sendbreak)) {
 	    zbeep();
             molbeg = -1;
@@ -2778,9 +2779,13 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    metafy_line();
 	    iforcemenu = 0;
 
-            if (cmd != Th(z_acceptandinfernexthistory))
+            if (cmd != Th(z_acceptandinfernexthistory)) {
                 modeline = setmstatus(status, saveline, savell, savecs,
                                       &modecs, &modell, &modelen);
+		/* if we are completing a quoted word, by this point we've
+		 * lost track of this, so put we back where it should be */
+		we = savecs;
+	    }
 
 	    if (nmatches < 1 || !minfo.cur || !*(minfo.cur)) {
 		nolist = 1;
@@ -2822,8 +2827,12 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    Menustack s = (Menustack) zhalloc(sizeof(*s));
 	    int ol;
 
-	    if (mode == MM_INTER)
-		do_single(*minfo.cur);
+	    if (mode == MM_INTER) {
+		Cmatch *cur = minfo.cur;
+		minfo.cur = NULL;
+		do_single(*cur);
+		minfo.cur = cur;
+	    }
 	    mode = 0;
 	    s->prev = u;
 	    u = s;
@@ -3279,6 +3288,7 @@ domenuselect(Hookdef dummy, Chdata dat)
                 strncpy(zlemetaline, origline, origll);
                 zlemetacs = origcs;
                 minfo.len = modelen;
+                we = wb + modelen;
             } else {
                 mode = 0;
                 comprecursive = 1;
@@ -3407,6 +3417,8 @@ domenuselect(Hookdef dummy, Chdata dat)
 		acc = 1;
 	    break;
 	}
+	if (was_inter)
+	    minfo.cur = NULL;
 	do_single(**p);
 	mselect = (**p)->gnum;
     }
@@ -3423,7 +3435,12 @@ domenuselect(Hookdef dummy, Chdata dat)
         clearlist = listshown = 1;
     if (acc && validlist && minfo.cur) {
 	menucmp = lastambig = hasoldlist = 0;
-	do_single(*(minfo.cur));
+	if (mode == MM_INTER) {
+	    Cmatch *cur = minfo.cur;
+	    minfo.cur = NULL;
+	    do_single(*cur);
+	} else
+	    do_single(*(minfo.cur));
     }
     if (wasnext || broken) {
 	menucmp = 1;
diff --git c/Src/Zle/compresult.c w/Src/Zle/compresult.c
index e3aa679ab2..3a73a2dd9f 100644
--- c/Src/Zle/compresult.c
+++ w/Src/Zle/compresult.c
@@ -588,9 +588,7 @@ instmatch(Cmatch m, int *scs)
 
     /* Ignored prefix. */
     if (m->ipre) {
-	char *p = m->ipre + (menuacc ? m->qipl : 0);
-
-	inststrlen(p, 1, (l = strlen(p)));
+	inststrlen(m->ipre, 1, (l = strlen(m->ipre)));
 	r += l;
     }
     /* -P prefix. */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.