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

Re: completion with globbing, take 2

Okay, with everyone's help, my problem is solved, but the solution
brings up lots of questions.

>   } Several days ago, I wanted to know how I could get zsh to respond to 
>   } 
>   } something *TAB
>   } 
>   } by replacing the * with the list everything that the completion system
>   } would return instead of everything * would match in the current
>   } directory.  I was told to do this:
>   } 
>   } zstyle ':completion:*' completer _oldlist _complete _match
>   } bindkey "^I" complete-word
>   More precisely, you were told that Andrej does that.
>   Andrej probably wasn't expecting you to use it verbatim, though, because
>   he didn't show you what his settings for the matcher-list style are.  


>   You
>   didn't say whether you have any settings for matcher-list; if you don't,
>   the _match completer won't do anything.

Sorry -- my original message failed to say that I was starting with
zsh -f with only the completion system loaded and no additional
customization.  This is why I was much more explicit in my second

Also, after reading the code, I don't believe it is true that _match
won't do anything without matcher-list set -- see analysis and
questions below.

>   } I want behavior more like what expand-or-complete does except that I
>   } want only what the completion system would return to be substituted.
>   That's what the _expand completer is for.  I believe you want:
>   zstyle ':completion:*' completer _oldlist _expand _complete _match
>   zstyle ':completion::expand:*' completions true

This almost worked, and it gave me enough additional information to
get the rest of the way there.  I now have the following:

  zstyle ':completion:*' completer _oldlist _complete _expand _match
  zstyle ':completion::expand:*' completions true
  bindkey "^I" complete-word

This seems to do exactly what I'm looking for.  We'll see whether I've
broken anything.

Removing any of _complete, _expand, or _match or changing the order in
which they appear breaks things.

Now I've analyzed the code and think I understand why this works, but
I'm not 100% certain.  Interestingly, ^X? doesn't work when I do

rmdir *^X?

but that's okay -- I just did setopt xtrace and then

rmdir *TAB

Again, I have just 1, 2, 3, a, b, and c in the currently directory
where 1, 2, and 3 are directories and a, b, and c are files.

Looking at the trace and the code, here's what I think is happening:

_main_complete is called.  It looks up my completers (_oldlist,
_complete, _expand, and _match) and calls then in order.  _oldlist
returns nothing, _complete returns nothing (since no files start with
'*').  Now it starts getting interesting.  _expand is called.  Since I
have set the completions style for expand to true, it sets
compstate[insert]=all which gives me the behavior I'm looking for with
respect to the replacement.  However, since the current context is not
expand-word:*, it does not call _complete and thus just returns 1.
Finally, _match is called.  I don't follow exactly what's going on at
the top of _match, but it doesn't matter.  Since I don't have
match-original set, _match sets compstate[pattern_match]='*' and calls
_complete again.  This time, since compstate[pattern_match] is set,
_complete actually does find matches.

So the right thing happens, but the path to the desired end seems
quite convoluted and counterintuitive to me.  I would never have
figured this out without the significant hints without having to spend
a lot of time grokking this code.

So there end up being two important commands here.  Without this one:

  zstyle ':completion::expand:*' completions true

the call to _expand doesn't set compstate[insert]=all.  Now if I look
at this:

  zstyle ':completion:*' completer _oldlist _complete _expand _match

If we remove _match, then _complete never gets called again with
compstate[pattern_match]='*', so it is essential.  If _expand appears
before _complete, then 

rmdir TAB

by itself inserts all the matches.

So, in summary, it seems that the only thing I'm using expand for is
to get compstate[insert]=all.  The only thing I'm using _match for is
to get _complete to be called with with compstate[pattern_match]='*'.
I can't do this with setopt glob_complete because if I do, then the
first call to _complete will generate matches when it is called before
the call to _expand, so I'll get the menu behavior instead of what I
wanted.  If I put _expand before _complete with these settings, I
always get all matches substituted (as if I had typed the *), which
renders the completion system pretty useless.

So, is my analysis correct?  Am I getting behavior that was intended
here, or have I just stumbled upon a way to do this?  I feel like I'm
getting the desired behavior through a series of coincidences and that
this could change or break pretty easily in the future....

Thanks for all the help.


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