Simple Software
In a previous article I talked a bit about software simplicity, or perhaps maybe the lack of it that exists in the world. One of my favorite parts of UNIX-like systems is the composability of command-line tools. Instead of having giant monolithic programs that do everything, you have various small domain-specific tools that all use a common interface — standard-input and -output. The following is a good example of this:
I have a YAML file on my system with various fun quotes, and I like to
have a quote-of-the-day that’s displayed when I open a terminal. I can
make use of the yq
program which is specialized in querying YAML
files to extract the list of quotes from the file. I can then use the
shuf
program to shuffle the quotes and pick one at random, and
finally I can use the tr
program to translate null-bytes to
newlines.
Notice how all three of these tools are very specialized to a specific
domain; they don’t try to reinvent the wheel, instead offloading the
tasks they aren’t specialized to do to other tools. yq
doesn’t
waste its time with functionality to shuffle lists of items because you
can do it with shuf
instead. This mindset of composability
allows you the user to create tools that are more powerful than they
could have been otherwise. Instead of hoping that all your tools offer
support for shuffling items and hoping that they can shuffle
things in the specific way you like, you can just use the one single
shuffling tool shuf
everywhere.
In my example I used shuf
to pick a random quote, but I could
similarly use it to pick a random song from a playlist, or to pick a
random image to use as a desktop wallpaper.
So with the composability of the UNIX environment being such a great
thing, it blows my mind that people are still writing software such as
kitty
that try to do absolutely everything in one giant
monolithic program instead of keeping focused to the task at hand and
allowing the user to extend their tools to add in the functionality they
so desire.
Some of you have surely already figured out what my issue is with what Kitty has done here, and why I think this is shitty software design. For those that haven’t, allow me to ask you a question: what would be better than Unicode input in your terminal?
The answer is simple: Unicode input everywhere.
Why should you be limited to Unicode input in your terminal? What if you’re texting your friend or sending an email, and want to include a Unicode symbol such as ‘™’, or want to properly refer to a site you visited such as Ta’ Ħaġrat? Perhaps you even have a friend whose surname is Mäkelä. Or maybe — you just want to send your friends a middle-finger emoji sometimes.
Wouldn’t it be nice if you had a global menu for Unicode input? Well turns out it’s actually really damn easy to do yourself, and renders all that Kitty code absolutely useless.
Writing the Script
The first thing you’re probably going to need if you want to have your own Unicode-input tool is a list of all the Unicode characters out there. Luckily for you, I already went through the hell that is the Unicode website to find it for you.
You can check out the file for yourself if you want. It’s got a bunch
of information in it — a lot of which is really useless for our
purposes. There are also a bunch of control characters and other things
in there that I personally don’t care for, so those can probably be
removed too. If it tickles your fancy, I’ve written a sed
script to clean up the input into something a bit nicer:
After processing, your Unicode data file should look something like this:
Now that you have your data file, we can begin scripting. The first
thing we want is to get all of the names of the Unicode characters.
Turns out that is a very easy task thanks to the cut
utility.
We can split each line on a semicolon and extract the second field with
a simple command, and then we can compose it together with dmenu
or your preferred clone of it to present the user with a graphical list
of items they can pick from:
Notice how we can simply compose cut
and dmenu
together,
and just assign the result to a variable. We can do this because these
are sane programs that read from standard-input, perform some simple,
basic task, and then print a result to standard-output. One tool to
parse a file, and another tool to let the user pick a selection.
We have the users selection now, the $name
variable holds the
name of the Unicode character that the user selected. All that we need
now is to actually get the Unicode character of our choosing.
Luckily this is also incredibly easy thanks to composition:
Congratulations! That entire script, which can be easily condensed down into only 2 lines of code is all you need to create a graphical interface that allows you to pick a Unicode character, and then copies your selection to your clipboard — and it was all done by taking simple tools and combining them to make a greater application.
So next time you’re developing software and want to add a new feature, just pause and think for a second. Do you need that feature? Can it be done in a better way? It is possible to generalize your feature to where it is useful outside of just your specific application? Don’t make the same mistake Kitty did. Allow me to leave you with this quote:
The Good, The Bad, and The Ugly
I would like to take a moment to point out some examples of composability done right, and some examples of features that are actively harmful in the pursuit of the UNIX ideal.
Ls — Bad
The ls
program is one of the most useful ones found in the UNIX
environment. It has a simple job to perform and it does it well. It
lists files in a directory. So what’s the issue then? Well let’s see
what happens when you combine ls
and cat
:
Ok… that looks about right. We invoke ls
and we get a listing
of the current directory, newline-separated. Well look at what happens
when we don’t compose it with another command:
Yeah… it decided to ‘prettify’ the output by putting everything on one
line. If the input contains enough items it columnates them really
nicely for you. Here’s an example of what it looks like when I run
ls
in my screenshots folder:
Now don’t get me wrong, I like the fact that I can see more than one
item per-line. But why is this specific to ls
? It could
actually be really useful to be able to take arbitrary input and
columnate it in such a form for easier consumption. The column
program does exist on Linux systems, but it is far inferior to what
ls
provides. In the ideal world, ls
would simply
display one filename per-line with a better column
command, and
you could compose the two to get a nicer viewing experience for your
files.
Tabbed — Good
What is one thing that almost all your graphical applications have in
common? They all have tabs. Your web browser has tabs, your terminal
probably supports tabs (and if it doesn’t, I bet you use tmux
).
Your code editors have tabs, and even modern email-clients have tabs.
Now let me ask you: why do we always reinvent the tab?
Suckless — the same people that brought us dmenu
— also created
a lesser-known application called tabbed
. You can
find it here.
It’s quite a simple piece of software. You simply run it together with
another program (such as the st
terminal) and it adds
tab-support to it. Not only does this reduce code-duplication, but it
also is beneficial for you the software user as it means you get a
consistent UI with consistent-behaviour and -key-bindings wherever you
go.