It’s time to replace Zsh with a saner shell because “unsetopt multifuncdef” breaks tab completion
Preface: I switched to Zsh roughly seven years ago. Prior to that I used Ksh93 for a decade. I’ve used many other UNIX shells prior to that (going back to approximately 1985 when I got my hands on my first AT&T SysV UNIX system). I’ve also used numerous shells on non-UNIX operating systems including IBM mainframes. So I like to think I’m not narrow-minded and parochial on issues such as which command shell is best.
On the zsh-users mailing list someone recently wrote about a zsh behavior that surprised them. The person ran
$ git add foo().bar
That created three functions named git
, add
, and foo
. That’s because Zsh by default allows multiple function names when defining a function. This is considered a feature by the Zsh community. Worse, it is enabled by default and you can disable it. I view both capabilities as two of the many ill-advised features that has turned zsh into a shell whose behavior is almost impossible to understand or predict.
After reading that message thread I figured it would be a good idea to disable this feature in my interactive shells so I added
unsetopt multifuncdef
to my ~/.zshrc
file. Imagine my surprise when a few days later after rebooting my computer and starting fresh shells finding that any attempt to invoke tab completion results in this error:
_main_complete:143: parse error near `()'
That’s because the /usr/share/zsh/5.0.8/functions/_main_complete
file contains this block of code:
TRAPINT TRAPQUIT() { zle -M "Killed by signal in ${funcstack[2]} after ${SECONDS}s"; zle -R return 130 }
Because zsh has no concept of modules or namespaces (other than function scope) changing an option in an interactive shell can readily break any function that is autoloaded by that shell; such as the completion functions.
Frankly, I’ve encountered too many such annoyances with Zsh. Even the developers who answer questions on the zsh-users mailing list frequently do the virtual equivalent of shrugging their shoulders and saying that some behavior or other is weird but it’s too late to change it. Not to mention too many of them seem to think it is a good thing that Zsh encourages writing code more cryptic than your typical Perl programmer would ever dream of. Such as this:
_comp_colors+=( "=(#i)${prefix[1,-2]//?/(}${prefix[1,-2]//(#m)?/${MATCH/$~toquote/\\$MATCH}|)}${prefix[-1]//(#m)$~toquote/\\$MATCH}(#b)(?|)*==$tmp" )
Or this:
list=(${${${(0)"$(git config -z --get-regexp '^alias\.')"}#alias.}%$'\n'*})
Bye-bye, Zsh. It’s time to switch to a saner shell.
P.S., Yes, I understand I could simply file a bug report to make the standard completion code robust in the face of a user unsetting that option. The point is that this is not an isolated incident. It reflects a fundamental problem with zsh trying to be all things to all people.
P.P.S., This bug apparently only existed in zsh v5.0.8 (the version that currently ships with Mac OS X 10.11 “El Capitan”). Great, someone noticed and fixed the problem quickly. That doesn’t negate my broader point that zsh simply has too many ad-hoc features that interact in surprising ways.