Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly install new completions in zsh?

The whole issue is discussed here and here. Since no one involved in those discussions was 100% sure about the issue, I'm asking for help here. (For the sake of completeness, I'll start from the beginning.)

Let's say we have two scripts (sourced in ~/.zshrc) that set up some completion logic for ZSH. Now based on what I learned, at some point in the script you need to call compinit and bashcompinit like this (copied from NVM completion script)

if [[ -n ${ZSH_VERSION-} ]]; then
  autoload -U +X compinit && if [[ ${ZSH_DISABLE_COMPFIX-} = true ]]; then
    compinit -u
  else
    compinit
  fi
  autoload -U +X bashcompinit && bashcompinit
fi

Apparently, according to ZSH manual, bashcompinit must be called after compinit, (not sure if it's relevant). Now the problem is, the moment the second script calls compinit, the logic coming from the first script is gone (i.e. no completion from the first script is available). A simple snippet to reproduce this is (copied from here):

complete -W "hello world" one
one <tab>   # to see autocomplete working
compinit 
one <tab>   # to see autocomplete NOT working

Someone proposed (here) something like below to solve the issue (by checking if compinit is already called before calling it):

if [[ -n ${ZSH_VERSION-} ]]; then
  if ! command -v compinit > /dev/null; then
    autoload -U +X compinit && if [[ ${ZSH_DISABLE_COMPFIX-} = true ]]; then
      compinit -u
    else
      compinit
    fi
  fi
  autoload -U +X bashcompinit && bashcompinit
fi

Another idea could be to call compinit and bashcompinit not in the custom completion script, but in ~/.zshrc (which hurts the automated installation process for tools like NVM).

I'd like to know what is the correct way to set up completion in general (or specifically with respect to calling compinit).

Thanks.

like image 754
Rad Avatar asked Nov 17 '25 03:11

Rad


1 Answers

First off, here's a good guide on how to write Zsh completion functions.

With that out of the way, let's look at how to install them.

Installing native Zsh completions

Let's say we have two scripts (sourced in ~/.zshrc) that set up some completion logic for ZSH. Now based on what I learned, at some point in the script you need to call compinit and bashcompinit

Nope, that's not what your script should be doing. Not your script, but the user should call compinit (in their .zshrc file) to enable Zsh's more advanced completion system. Additionally, it should be called only once for each shell instance.

(Note: I say "more advanced", because a more basic, Bash-like completion system is enabled by default in Zsh, but it's pretty much deprecated. Do not bother with it; the vast majority of your users will already have compinit in their dotfiles, which provides a much better user experience, even though it's not enabled by default. Yes, Zsh ships with poor, archaic defaults in the name of backwards compatibility. Nearly all its newer features are opt-in, unfortunately.)

This is the proper way to add native completion functions to Zsh:

  1. Do one of the following:
    • To install completion files for all users, create symlinks in /usr/local/share/zsh/site-functions/ (which is in every Zsh user's $fpath by default) to each of your completion functions. If you can't write in that dir, then choose the following option instead.
    • To install completions for the current user only, add symlinks to your completion files in ~/.local/share/zsh/site-functions/ and tell the user to add that dir in ~/.zshrc to their $fpath:
      fpath=(
          ~/.local/share/zsh/site-functions/
          $fpath
      )
      
  2. Tell the user to make sure the following gets called once from ~/.zshrc after setting their $fpath:
    autoload -Uz compinit
    compinit
    
    (Note: Many Zsh frameworks & plugin managers include a call to compinit on startup. Calling compinit more than once increases startup time significantly and erases completion functions added programmatically. You might want to make your users aware of this, to save yourself needless bug reports.)

compinit will then automatically pick up your completion functions from the user’s $fpath.

Installing Bash completions in Zsh

Apparently, according to ZSH manual, bashcompinit must be called after compinit, (not sure if it's relevant).

Yes, it's relevant, but no, not in the way you think it is. Among other things, bashcompinit defines the function complete, which emulates Bash’s complete builtin and can be used to add Bash completions to Zsh. Like compinit, bashcompinit is meant to be called only once per shell.

To add Bash completions to Zsh:

  1. Install your Bash completion files as usual.
  2. Tell the user to make sure that the following gets called once from their .zshrc file: autoload -Uz compinit bashcompinit compinit bashcompinit
  3. Give the user the same calls to complete as in Bash and tell them to add those to ~/.zshrc after the code above.

However, Zsh's completion system is much richer and more powerful than Bash’s. If at all possible, I would recommend supplying a native Zsh completion function instead.

like image 117
Marlon Richert Avatar answered Nov 18 '25 21:11

Marlon Richert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!