Zsh: separate completion for command names and filenames

autocompletecommand lineterminalzsh

I do not want filename tab completion to prioritize the start of the file name. For example, given the filenames red_blue.txt and blue_red.txt, I do not want vim red tab to prioritize red_blue.txt

This can be accomplished by using:

zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z} l:|=* r:|=*'

However, this behaviour will also apply to the tab completion of command names, which I do not want. For example, typing nit tab looking for the command "nitrogen" will also match commands like mkinitcpio, xinit, compinit, and various others.

For commands, I would like the completion to use:

zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'

How can I have zsh tab completion treat command names and filenames differently?

Best Answer

Generally speaking, you need to refine the zstyle call so that it doesn't apply to all completions, but only to file completion. For an ordinary completion, the syntax of the context specifier is :completion:WIDGET:COMPLETER:COMMAND:ARGUMENT:TAG.

  • WIDGET is only set by some special widgets, leave it generic (*).
  • COMPLETER is complete for normal completion, and can have other values for tasks such as autocorrection.
  • COMMAND is typically the name of the command whose arguments are being completed. More precisely, it's the word after compdef. Some complex commands change it while completing subcommands. For special places in the shell syntax, it's a context name between dashes, such as -parameter after a $ or -command- for the first word in a command.
  • ARGUMENT is typically something like argument-3 for the third non-option argument or option--foo-1 for the argument to the option --foo.
  • TAG is used internally by some completion function, often but not always one of the conventional tag names.

In zstyle declarations, more specific declarations take precedence over less specific ones.

  • A declaration with more colons (:) is more specific than one with fewer colons.
  • With an equal number of columns, a declaration is at least as specific than another if each colon-delimited part is at least as specific the corresponding part in the other. For a each part:
    • * is more specific than anything else.
    • Anything with wildcards is more specific than a simple string without wildcards.

Thus, in general, to make an exception for command names, just add another zstyle declaration that's specifically about commands.

However, there's a twist: matcher-list is applied globally, not in the context of completion. In your case, as long as you only want a single matcher, you can use matcher instead.

zstyle ':completion:*' matcher 'm:{a-zA-Z}={A-Za-z} l:|=* r:|=*'
zstyle ':completion:*:*:-command-:*' matcher 'm:{a-zA-Z}={A-Za-z}'