zsh, you complete me

I just started a new job.

On my first day, I received my new squeaky clean computer and started setting it up.

In addition to installing software, adjusting configurations, and all the rest of the usual setup tasks found in any startup’s setup documentation, there was one more important step to make my new computer really “my own” — I mean, I can’t write proper code without all my favorite shortcuts, now can I?

One of the first things I did was downloading zsh, my favorite shell program, and create an alias file.
**While we’re on the subject of zsh, I recommend installing “Oh my zsh” as well. It’s not a must, but all of its themes and plugins does make for a superior shell experience**

At my last job, I worked on two big projects (for the purposes of this post I will refer to them as luke and leia). Each of them had its own code repository and virtual environment.
In order to make my life easier, I used two commands that initiate a new shell for working on a specific project -

alias luk = ”source ~/virtual_envs/luke/bin/activate; cd ~/git_repositories/luke”
alias lei = ”source ~/virtual_envs/leia/bin/activate; cd ~/git_repositories/leia”

Whenever I wanted to switch between environments, I could do so using a single command.
Super useful!

In my current job, we’re working with a micro-services architecture, which means many(!) code repositories and environments that I need to be able to initiate and switch between quickly and easily.

Obviously, it wasn’t practical to define a separate alias for each of the many different environments, so I needed to come up with a different method.

The first solution that came to mind was writing a bash function since it’s a very intuitive and convenient way to execute shell commands.

This is the first function I wrote -

This function accepts the name of a project, initiates its environment, and switches to its work folder.

For example, the command ‘repo luke’, would execute these commands -

source ~/virtual_envs/luke/bin/activate; 
cd ~/git_repositories/luke

Exactly like my old alias used to do.

This was all well and good, but when I tried to use my function to switch between the different projects I came across a problem — the function was working correctly, but it was missing one of the most important functions that we all take for granted in shell commands — the autocomplete.

When I work, I’m used to typing only the start of commands, and then to just hit the TAB key until I get the right completion suggestion, but this function didn’t have completion suggestions, which meant that in order to use it, I had to know the names and spellings of each different project by heart (that way madness lies!).

I googled it for a while and tried to find a way to add auto-completion to my function, but I wasn’t able to find any helpful guides on the subject, so I decided to settle, and instead of using actual auto-completion, use this amazing plugin — zsh-autosuggestions. It’s a very cool plugin for zsh that uses the shell history to suggest command completions based on recently used commands.

I went on my merry way home, but this autocomplete issue kept bugging me, and I found myself scouring the internet all night long, looking for an answer, until I finally managed to accumulate enough knowledge from several guides, and create a working completion function!

The first step was to understand how to even create a completion function in zsh.

A completion function is a function that defines various types of possible completions.
To let zsh know which function we want to complete, you need to define the connection between them like so -

compdef _repo repo

Most guides instruct you to create a separate file for your completion function and put it in the appropriate system directory, but for my purposes, this was total overkill, so I added my completion function in the same file as my main function.

For starters, I wanted to see if I can create a working completion, so I played around with static completion -

There are many types of completions suggestions, and I almost got lost looking for the right one until I found this guide, which helped steer me in the direction of the most easy-to-use type — the ‘arguments’ completion.

Using the ‘arguments’ definition returns a set of optional completions for each function argument, and it can be used statically like I’ve shown above, to suggest completions for scripts that take specific arguments.

Now, when I typed the ‘repo’ command in my terminal I got three possible completions — a1, a2 and a3.

Up until now, everything was pretty straight forward. The problem was that I didn’t want to use static completion.

Yes, I could maintain a list of project names and it wouldn’t be the end of the world, but it wouldn’t be the truly automated completion I wanted.

What I wanted was for the completion function to suggest all the file names in my git_repositories folder for me to choose from. This might seem like a very obvious thing to expect, but again, this subject is very poorly documented, and I had trouble figuring out how to accomplish it.

This started a fairly long session of trial and error, with a myriad of different configurations, parameters, and many other crazy solutions Stackoverflow had to offer.

I won’t bore you with all my different attempts, I’ll only tell you that I came out victorious, with a working function that automatically suggested the names of all the projects in my folder.

Using the $ operator evaluates and returns the value of the command inside the brackets — ls — which returns the names of all the files in the git_repositories folder. This list is used as the list of completion suggestions for the repo function, exactly as I wanted.

All in all, this took me some time to figure out, but I think it was time well-spent since now I know that in the future, every bash function I’ll write would perform much more smoothly.

XKCD always know how I feel https://xkcd.com/1205/

Writing shell functions is fun, and I recommend each and every one of you to write your own cool functions.

You can view the source code on GitHub — https://github.com/mgershovitz/maya_solves/blob/master/dot_files/.repo_switch


This article was originally published on https://mayareads.blog/

Maya Gershovitz Bar

Written by

Software Engineer by day, blogger when ever I have free time — https://mayareads.blog/

BigPanda Engineering

The most awesome technical posts, by BigPanda engineers for engineers

More From Medium

More from BigPanda Engineering

More from BigPanda Engineering

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade