Do not expand wildcard on tab completion

tab completionwildcardszsh

I'm wondering how I can configure zsh to not expand the wildcard in a filename. For example I have a directory of a couple hundred files named as following:

a.foo-bar a.foo b.foo-bar b.foo c.foo-bar c.foo

I would like to be able to perform a tab completion matching after a wildcard, so

cat *.foo-< tab > => cat *.foo-bar

This is what I get instead:

$  cat *.foo-
=> cat a.foo-bar
   file
   a.foo-bar b.foo-bar c.foo-bar

Best Answer

One option is, that you replace the default key binding for the TAB key

bindkey "^I" expand-or-complete

with

bindkey "^I" complete-word

which won't expand a * to all matching files, but leaves the star untouched. This way you get rid of the endless list, but it still won't complete the globbing expression.

To solve this is much more tricky. You have to write your own completion widget (see man zshcompwid).

  1. Let's begin with the definition:

    zle -C complete-glob menu-complete compglob
    

    This introduces the new widget complete-glob, which behaves like menu-complete and uses the shell function compglob to generate the matches.

    [ Note: If you don't want the menu-completion, use the much more rudimentary options complete-word or list-choices ]

  2. Bind this new widget to a convenient shortcut, like CTRL+K:

    bindkey "^K" complete-glob
    

    Don't bind this to TAB, as the widget in its current form only completes files!

  3. Define the shell function compglob as follows, which does the actual work:

    compglob () {
        setopt localoptions globsubst
        compset -P '*'
        files=(${IPREFIX}*) 
        display=(${files/${IPREFIX}/${(q)IPREFIX}}) 
        glob=(${files/${IPREFIX}/}) 
        compadd -d display -- $glob
    }
    
  4. Demonstration:

    $ touch a.foo-bar a.foo b.foo-bar b.foo c.foo-bar c.foo
    $ cat *.f<CTRL+K>
    $ cat *.foo
    \*.foo      \*.foo-bar
    

    The only flaw I see is the \ in front of the star in the presented list. But this is only an optical flaw, as the completion is correct: *.foo or *.foo-bar.

Related Question