Zsh – Make Tab Exit Menu-Complete and Perform Completion on Last Entry

zsh

I'm trying to switch from bash to zsh, but there is one main thing that I find inconvenient with zsh. I have unset the menucomplete option, bound tab to expand-or-complete-prefix, and bound alt-s to menu-complete.

However, once I invoke menu-complete by pressing alt-s, subsequently hitting tab continues to cycle through the menu-complete entries instead of exiting menu-complete and performing completion on the last displayed entry like in bash. Main reason why I want this is so I can quickly "go into" the last displayed directory in the case that I want to cd/ls into multiple nested directories.

I'm using zsh version 5.8 and have the following in my ~/.zshrc:

setopt autolist
unsetopt automenu autoremoveslash listambiguous menucomplete
bindkey '^i' expand-or-complete-prefix # Bind tab
bindkey '^[s' menu-complete # Bind alt-s to menu-complete

Here is a working example:

$ mkdir dir dir/a dir/b dir/b/d dir/c
$ ls dir/ <alt-s>
$ ls dir/a/ <alt-s>
$ ls dir/b/ <tab>
$ ls dir/c/ <tab>
$ ls dir/a/ <tab>
$ ls dir/b/ <tab>
$ ls dir/c/
...etc.

Instead, I want tab to "go into" dir/b/ like in bash:

$ ls dir/ <alt-s>
$ ls dir/a/ <alt-s>
$ ls dir/b/ <tab>
$ ls dir/b/d/  <-- Pressing tab exits menu-complete and immediately goes into the currently displayed directory.

Does anyone know how I can obtain this behavior? Thanks so much for your help!


Edit:

I found a somewhat mitigating solution with menuselect in zsh/complist by adding the following to ~/.zshrc:

zmodload -i zsh/complist
bindkey -M menuselect '^i' accept-line
zstyle ':completion:*' menu select=1

Now, pressing tab during a menu-complete operation exits menu-complete. However, it doesn't "go into" the last displayed directory and I have to press tab again to do autocomplete within that last displayed directory. So two tab presses in zsh instead of one in bash for the same functionality.

Note there is a different menuselect option:

bindkey -M menuselect '^i' accept-and-infer-next-history

which "goes into" the last displayed directory with one press of tab, but performs menu-select in that directory instead of just normal tab completion like what I want.

If anyone knows how I can get this functionality down to just one press of the tab key, would be much appreciated. Would it be possible to write a custom zle command for menuselect, for example, an accept-and-complete-next-history?

Best Answer

With the zsh-autocomplete plugin, you can get the following:

  1. Type anything and completions are automatically listed.
    • No need to press Tab.
    • Type more to narrow down the choices.
    • This supports fuzzy completion, so you can type any part of the word you're looking for, as long as what you type next is further to the right in the target word.
  2. Press Tab to insert the top completion.
    • If this is a directory, sub-directories are automatically listed.
    • Rinse, lather and repeat.
  3. If you cannot get the completion you want at the top, then you can use the Arrow Keys to navigate to it.