A few months ago, Gabriel Gonzalez wrote an excellent article on creating useful tools with Haskell. He used an example of a small CLI tool that aligns the equals signs of a multi-line text input. The article wrapped up by integrating this tool into vim.
I love the overarching concept in the article: create small tools that are useful in multiple contexts. It's directly inline with the Unix philosophy.
The only problem is that I am an Emacs user. So in this article, I wanted to show how I get the same effect in Emacs. And demonstrating how to take CLI interaction in the text editor even further.
Take Action on a Region
In his article, Gabriel uses the command he created to take action on a region. This involves selecting text and executing a CLI command on it's contents -- replacing the region with the command's output.
For this example, we will use the command from Gabrial's article, ~align-equals~. To see the full implementation and learn a little Haskell, you can read his article. Or you can follow along while keeping the below command's usage in mind:
Emacs has a built in command,
shell-command-on-region, which executes the
specified command on the selected region. This would work for our example, but
it requires two commands. First you execute
shell-command-on-region, then you
provide the command you want to run. If the cli command needs flags or
arguments, it becomes tedious to put in every time. Wouldn't it be nice if we
could select a region and only run one command?
To accomplish this, we need a wrapper. This implementation combines a couple
elisp components. We start our function by expecting the beginning and ending of
the region as arguments. The region(
r) flag passed to
that the correct arguments get passed in.
interactive lets us invoke our function from anywhere, and supports many more
options, which I encourage you to explore on your own.
With our region bounds ready, we call
executes a shell command on a region specified by the beginning(
e) arguments. We also supply the shell command to execute, and two
flags which make the command replace the selected region.
We can expand the CLI command in this wrapper to take any number or arguments
and flags. No matter how we change it, we can still invoke it with one command:
Take Action on a Buffer
A common scenario I run into, is wanting to run a tool on the whole file. This normally comes in two variants. I either want to modify the content of the file in some way, like using a beautifier. Or I want to produce an output based on the file content, like getting totals or showing lint errors.
Again, there are built in ways to do this, but a wrapper lets us do more with our shell commands.
Replacing Buffer Contents
This time, we don't need to pass any arguments to
interactive. Instead, we use
~shell-command-on-region~ with the results of calling
~point-max~ -- the start and end of the buffer, respectively.
align-buffer aligns the entire buffer without the need to manually select a
region. This makes the it less error prone and easier to execute. We can't
select the region incorrectly, because we don't need to anymore. We only need to
Displaying The Output of a Shell Command
For this examples, let's use
eslint is different from our previous command. It takes a file name, not the
buffer contents. This means we will need to use some new functions.
Once again, we don't accept any arguments and pass nothing to
get the buffer name by using the
buffer-name function. Once we have the name,
we merge it with the string
eslint to create the final command, and pass it to
~shell-command~. This logs the output to a dedicate shell command buffer.
Now we can execute
executing the command.
Seeing the results is a good start. But
eslint comes with a handy
that automatically fixes simple problems.
To add this flag you only need to change the string you pass to
With that change, the simple issues get fixed automatically and we get a log of the complex issues to fix manually.
Running Command Automatically
From the last example we have a useful
run-eslint function. But we need to
remember to run it every time we want to check a file. Let's reduce our mental
burden, and let Emacs automatically execute this function every time we save a
There are two facilities that makes automatic function execution precise and safe: modes and hooks.
Modes allow us to know what context we are in. When we open a new file, a number
of modes can activate. For our example, there is a built in
Once we are inside
js-mode, we will add another hook -- this time to an
eslint can fix some issues for us, we will run it before the
file is saved by hooking onto the
In the implementation, we use
add-hook to listen to the two action described
js-mode-hook only needs the function to execute when the mode is
before-save-hook needs the
LOCAL option. This only runs the
action in the buffer it was activated in. Without this flag,
eslint would run
I only scratched the surface of what's possible with Elisp. Projects like magit provide amazing examples of extending basic CLI tools.
Magit is a wrapper around git. It doesn't change what git does. Instead, it adds on text manipulation and file awareness that Emacs is good at. If you need inspiration for how to integrate other tools into Emacs, look no further.
I hope that this article provided some inspiration and a few new tricks. Happy hacking.