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

Re: [BUG] complist interactive mode overwrites command line



Another thread rising from the grave:

On Sat, Jul 16, 2022 at 11:11 AM Bart Schaefer
<schaefer@xxxxxxxxxxxxxxxx> wrote:
>
> > On Wed, 4 Aug 2021 19:30:10 +0300 Marlon Richert <marlon.richert@xxxxxxxxx> wrote:
> > > % zmodload zsh/complist
> > > % bindkey '^I' menu-select
> > > % MENUMODE=interactive
> > > % touch test{1,2}
> > > % : ; foobar
> >
> > >     ^ 1. Type the above line in its entirety.
> > >       2. Place the cursor before the ;
> > >       3. Press Tab.
> > >       4. Press Enter.
> > > % : test1 bar
> > >     ^ Completion is written over existing buffer contents.
>
> The issue seems to be that interactive mode assumes you're going to
> use it interactively -- as in, type a single character at a time until
> you've reduced the set to only one match -- so it only adjusts the
> buffer spacing on single keystrokes.  When you accept with TAB (or
> ENTER) domenuselect() is relying on do_single() to fix everything up,
> but the state required by do_single() is not fully populated.

This entirely seems to come down to minfo.len causing the wrong thing
to happen in compresult.c:do_single(), specifically here:

    /* If we are already in a menu-completion or if we have done a *
     * glob completion, we have to delete some of the stuff on the *
     * command line.                                               */
    if (minfo.cur)
        l = minfo.len + minfo.insc;
    else
        l = we - wb;

    minfo.insc = 0;
    zlemetacs = minfo.pos;
    foredel(l, CUT_RAW);

In normal menu completion, when entering the menu, the first match is
already inserted on the command line.  This code is used in common by
interactive completion, so when accept-line is invoked, minfo.len is
the length of that first completion.  However, what minfo.len is
supposed to represent is how many characters have already been
inserted on the line, which in this case is zero because interactively
nothing gets inserted until you type a matching character.  Thus the
foredel() call there deletes what it thinks is the completion already
on the command line, but instead deletes other stuff after the cursor.

This can trivially be fixed for Marlon's specific example by setting
minfo.len = 0 in the "if (first)" block in domenuselect(), but that's
only correct for this specific case.  Anything that resets the
appearance of the line -- such as hitting TAB again after accept-line,
because (as in the "user defined widget doesn't execute suffix
removal" thread), we are actually still in menu completion at that
point even though we've left interactive completion -- also loses
track of the fact that the original line is back the way it was, and
so minfo.len becomes wrong again and things like navigating the menu
with the arrow keys start foredel()-ing too much.

Interactive completion gets in its own way here a bit, because there
are several cases where it attempts to restore the original state and
gets it subtly wrong.

If anyone thinks they have a better grasp of all the special cases and
widget handling in complist.c, please chime in.  Of course 2/3 of the
lines of complist.c are Sven W's and a good chunk of the rest is
comments from other people trying to explain what Sven did, so I'm not
very hopeful.




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