Anyone who has been in my vicinity for more than 5 minutes will know that I LOVE Emacs. Probably more than any person should love any piece of software, I spend countless hours tweaking my config and have even attended some Emacs meetup groups. So it should come as no surprise that I’m writing this blog today trying to convince you, the reader, to use this text editor.
The best feature of Emacs is how easy it makes programmatically interacting with text. As programmers and sysadmins, we spend most of our day interacting with text on a computer. So having a platform for automating text interaction can save us a lot of time every day. Most of us will already have some platform be it Bash or Python that we prefer for text automation, but I hope that with this blog I can convince you that Emacs has features that can save you hours of work that you would otherwise require with those other tools. I’m also writing this so I have a post that I can point people to and save their souls from the eVils of Vim.
Software Licensing is Hard (The Problem)
Anyone involved with Free and Open Source Software (FOSS) will know what a legal quagmire software licensing can be. I mean, we have entire organizations that are dedicated to interpreting and defending FOSS licenses and sometimes they even get it wrong. So, anytime I go to a Linux Fest and there is any talk being given about Software Licensing, I make sure that I find a way to attend it. Recently, at Ohio Linux Fest, I attended one such talk by Mike Dolan from the Linux Foundation on this topic and found I had been negligent in one area of license maintenance.
Mike reported that one of the best pieces of advice the Linux Foundation offers projects is to require license headers in their source files. The Linux Foundation considers this as an important step in giving yourself legal ground to stand on when defending your rights and code. This was news to me as I had been just shipping a license file with my code and calling it a done deal. However, hearing Mike speak about it convinced me that this wasn’t enough and as a maintainer of a few Open Source projects, I feel obligated to get licensing right to protect the rights of my code and those of my contributors. Unfortunately, adding these headers to my new files alone sounded like a mountain of work…not to mention that none of my projects had been following this advice up to this point. So every project I have been working on had a ton of files that I would have to find and license.
Enter Emacs (The Solution)
So while pondering my predicament I brainstormed a few solutions but realized pretty quickly that I could simply write a new Emacs command to license the file I’m in, regardless of programming language. Emacs already knows how to comment text with the correct style based on the current programming language of that file…which meant that I didn’t need to maintain my own list or do file type detection. The Emacs maintainers had done that for me! So first, I set out to write a command that simply licenses the current buffer. It was a much dirtier version of this (Side Note: I’m a Spacemacs user so I follow their conventions, if you’re wondering why the functions are named as they are):
(defun chasinglogic/license () "Insert the GPL license header." (interactive) (progn (goto-char (point-min)) (insert (format (chasinglogic//get-license-template) (format-time-string "%Y" (current-time)) user-full-name user-mail-address)) (fill-region-as-paragraph (point-min) (point)) (comment-region (point-min) (point)) (insert "n") (goto-char (point-min))))
Let’s go through this line by line. Then, we can talk about alternative implementations and why writing this in Emacs matters (and what you can’t get from using this with vim or some other language). First, for the uninitiated:
(defun chasinglogic/license ()
"Insert the GPL license header."
This defines a function with a docstring. The
(interactive) bit tells Emacs that this is supposed to be a user command invokable via
: for Vim). Next:
progn is a special macro which basically says “do these things in order.” Lisp is a functional programming language; so, normally, it would “return” after the first function.
progn lets us imperatively do multiple calls in sequence without accidentally ending the function early or in an unexpected way.
(insert (format (chasinglogic//get-license-template)
(format-time-string "%Y" (current-time))
(goto-char says “place the cursor at some point in the buffer.” Emacs calls where the cursor can be a “point,” so,
(point-min) returns the minimum place the cursor can go. When used in combination,
(goto-char (point-min) places the cursor at the beginning of the buffer. Next,
(insert will simply insert a string as though it was typed or pasted wherever the cursor is.
(format formats a string with variables much like
printf in other languages, except it returns the formatted string as a value. The variables
user-mail-address are simply my name “Mathew Robinson” and email “email@example.com.” These are Emacs default variables that various Emacs commands use, so it made sense to piggyback on that. The string that
(format uses is returned by
(chasinglogic//get-license-template). It looks like this:
"Copyright %s %s <%s>. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
So, it will substitute in the current year, my name, and my email into this string where the
%s‘s are in that order. Then it passes that formatted string to insert with the cursor at the beginning of the buffer. Thus pasting the correct license header at the top of the file.
(comment-region (point-min) (point))
(fill-region-as-paragraph (point-min) (point))
Next, we’re commenting the newly inserted license header then “fill”-ing it. In Emacs, the term fill is for making the text fit into the fill-column width. For me, this is 80 columns which is something of a standard but it can be customized if you so choose.
Finally, we simply insert a final line break to help separate the license from the rest of the file and set the cursor to the beginning of the file:
But What about VimScript?
So after reading all this, you may be thinking “I can do that in Vimscript.” And you’d be right, which doesn’t sound like I’m making a great case for Emacs, but I promise it’s coming.
The first point I’d make about Vimscript or any other language, and this point is rather weak, is that generally, the solution will be more complex. Vimscript, in general, is a less clean language (IMO) than elisp. With anything else, you’d be writing a whole tool or script to do this one small thing that would require you to leave your text editor to… well… edit some text by inserting a license header. This seems to break my flow more than it’s worth to me but YMMV.
Now, the real argument, and why I think Emacs is such a great choice for this, is integration with other Emacs tools and libraries.
More Emacs! (The Real Solution)
If you remember from earlier, I was originally concerned with automating this for future projects, but was unsure how I could easily automate fixing old projects. With the code above, I can easily insert a license header into the top of my current buffer, but I’d still have to find all the files in my projects that don’t have the license header and manually call my new function. Enter Projectile.
If you’ve been using Emacs for any amount of time, you’ve probably heard of Projectile, and if you haven’t, the tl;dr is that it’s a code project interaction library for Emacs that offers some nice commands like
projectile-find-file that I use every day.
When you invoke
projectile-find-file, it shows you a searchable list of all the files in the current project. I got to thinking “Hey, if projectile can get that list, can I just get that same list and iterate it calling my new command?”. Spoiler alert: the answer is a resounding “yes.”
One of the best parts of Emacs is the help system; so, the first thing I do is hit
C-h f (or the
describe-function command), search for projectile-find-file and see the following:
projectile-find-file is an interactive autoloaded compiled Lisp function in
It is bound to C-c p f, <menu-bar> <tools> <Projectile> <Find file>.
(projectile-find-file &optional ARG)
Jump to a project’s file using completion.
With a prefix ARG invalidates the cache first.
If I click on the ‘projectile.el’, Emacs opens up the source code for that that function:
(defun projectile-find-file (&optional arg)
"Jump to a project's file using completion.
With a prefix ARG invalidates the cache first."
Which wasn’t super helpful…so I jump to the internal function it’s calling using imenu and see:
(defun projectile--find-file (invalidate-cache &optional ff-variant)
"Jump to a project's file using completion.
With INVALIDATE-CACHE invalidates the cache first. With FF-VARIANT set to a
defun, use that instead of `find-file'. A typical example of such a defun
would be `find-file-other-window' or `find-file-other-frame'"
(let ((file (projectile-completing-read "Find file: "
(ff (or ff-variant #'find-file)))
(funcall ff (expand-file-name file (projectile-project-root)))
Which shows me that it’s calling
projectile-current-project-files to get the list of project files. I go ahead and describe that function too for good measure:
projectile-current-project-files is a compiled Lisp function in ‘projectile.el’.
Return a list of files for the current project.
Perfect. This will give us the list of files we want to license! So I write a much buggier version of this function:
(defun chasinglogic/license-project (&optional match)
"License the current Projectile project. Will skip certain classes of files
(.git .md etc.). Additionally, if match is given the filename will be checked
against it as a regex to determine if the license should be inserted or not."
(find-file (concat (projectile-project-root) x))
(lambda (f) (chasinglogic//should-license f match))
For those new to functional programming and lisps, you can think of
-map as a for loop over the list.
-filter simply finds the files that return true from the function
chasinglogic//should-license so we don’t try to license files that we don’t want to hit. (Originally I destroyed the local git history for a project since I inserted the GPL at the top of all of git’s internal files :D). It’s also important to note that
-filter are not part of the standard Emacs distribution. They are part of a library called dash that you can include with
(require 'dash) once you have it installed (spacemacs provides it out of the box so I get it for free).
Just like that, my small Emacs command went from a tiny solution for local files into a full-blown project licensing machine! This is the real power of Emacs: its ability to discover and dig down into everything it can do via the describe commands that allows you to compose your automation with those of others to build whatever you want.
For those curious, you can steal all of this code in full context from my dotfiles as a Spacemacs layer. I’m currently refactoring out these functions and expanding on them as their own Emacs package that I hope to publish on MELPA with tests. But I wouldn’t hold your breath since I’m working on revamping open source project management right now, which I consider to be a better value add for everyone.
If you’ve made it this far, thanks for reading this lengthy post! I hope that on some level, this at least helps you understand why some of us adore this 30+ year old piece of software.