zsh-dwim: Now With Faster Startup Times, Modular Configuration, and More

| Comments

It has been quite a while since I’ve made any updates to zsh-dwim. A few weeks ago, a feature request from PythonNut showed up in zsh-dwim’s issue tracker on GitHub. He asked me if I wouldn’t mind splitting the implementation and configuration details of zsh-dwim out into separate files.

I thought this was a great idea, and I’ve had similar thoughts in the past. What I was really interested in doing was breaking up the long, monolithic set of transformation definitions into separate files. That way you could easily disable each piece of functionality individually.

I’ve been thinking this would be a good idea for quite a while, but it hasn’t been a priority for me. As far as I knew, I was the only person using zsh-dwim. When the feature request came in, I was happy to see a handful of stars and forks on the zsh-dwim project. That was more than enough of an excuse for me to put in a bit of time on the project.

What is zsh-dwim?

I haven’t written about zsh-dwim in quite a while, so it might be a good idea to explain what it is. The “dwim” in zsh-dwim stands for “Do What I Mean.”“ I borrowed the term from the many Emacs functions with “dwim” in their names. The idea is that when you’re writing a command, or you just finished executing a command, you can just hit one key and zsh-dwim will try to guess what you want to happen.

If you just created a new directory or untarred a file, hitting the zsh-dwim key will attempt to cd into the new directory. Maybe you tried to echo a value into a file in /proc or /sys, and you forgot that only root can do that. Hitting the zsh-dwim key will convert your > into a | sudo tee for you.

If you hit the zsh-dwim key on an empty command line, it will apply its logic to your previous command. If you are already in the middle of editing a command, then zsh-dwim will attempt to apply its logic to it. zsh-dwim will also try to move your cursor to the most logical position.

The commands aren’t executed automatically. You still have to hit the enter key. I’m a big believer in the principle of least surprise. I’d much rather see what is going to be executed before I commit myself to it. I am not a big fan of the sudo !! idiom. That’s why zsh-dwim’s fallback simply adds a sudo to the front of the command. I feel better seeing the actual command that I’m about to run with root privileges.

The more modular design

First of all, as per PythonNut’s request, the configuration has been separated out from the inner working of zsh-dwim. This will make it easier to choose your own zsh-dwim key—my choice of control-u is surprisingly controversial!

I’ve also grouped the related transformations together and put them into separate files. All the transformations are enabled by default, and each file is sourced in the config file. If you don’t like some of my transformations, you can easily comment them out.

zsh-dwim is a bit smarter

I also added some checks to make sure that useless transformations are never used. To accomplish that, zsh-dwim now checks to make sure the appropriate commands are installed before enabling certain transformations. You don’t want to be offered apt-get and dpkg commands on a Mac, and you don’t want to be offered dstat commands when you only have vmstat available.

I put these checks in the config file. I waffled on this decision quite a bit. The config file would be cleaner if I put these checks in the files that define the transformations. That way you’d only have to comment out a single line to manually disable them, but I thought that might get confusing. I didn’t want someone to enable a set of transformations and wonder why they’re not working.

Speeding things up a bit thanks to PythonNut

The new brains in zsh-dwim made zsh startup times slower by almost 0.01 seconds on my computers. That’s almost 15% slower on my desktop and about 5% slower on my laptop. This was caused by calling out to the which command to verify if certain programs were installed.

I wasn’t too happy about this. I open terminal windows dozens, maybe hundreds, of times each day. I like my terminal windows to be ready instantly. When I was using oh-my-zsh, I put in some work to improve its startup time. It was pretty slow back in 2012, and I submitted a patch that shaved 1.2 seconds off of oh-my-zsh’s startup time. By comparison, the extra 0.01 seconds I added to my Prezto startup time was minimum. I still didn’t like it, but it was acceptable for now.

Not long after I applied my changes, PythonNut sent me a pull request on GitHub that replaced all my subshell calls to which with calls to hash. I had no idea about this functionality in zsh, and his patch completely negated any performance penalties I may have caused.

It could probably be argued that this isn’t really a speedup, and that zsh-dwim is just back to approximately its previous speed. I am still calling it a speedup. Functionality is slightly improved, and I was even able to replace some calls to which in my own startup scripts with calls to rehash, too.