Prelude
Every few months or so, I get an episode of .emacs.d
OCD, where my setup suddenly seems hopelessly disorganized and non-optimal, and I can’t do another thing until I fix it. I went through such an episode recently, causing me to spend hours looking through various setup strategies, but ultimately not finding that je ne sais quoi I was looking for.
The whole exercise made me step back and reflect on what it is that causes me the most trouble when trying to organize an emacs config, and after a moment of reflection, I came up with 1) Where should I put things? 2) How should I init things?
It’s Load Time!
Where to put things is a problem that spans well beyond emacs configuration, and may well be the third-hardest topic in computer science, so I’ll get back to it. Number 2, however, is a little more particular to emacs. The issue is this: If you’re like me, you’re using dozens of emacs plugins to optimize your environment, and layering a handful of personal customizations and settings on each. That’s great. That’s what emacs is all about. But then, there’s autoloading.
We emacsians spend a good bit of time biting our nails over our favorite editor’s startup time, and make tremendous efforts to do as little as possible at startup time, instead letting packages load as needed.
Problem is, some package configurations require packages to be loaded while others don’t. Some packages don’t do autoload right. Sometimes you configure an autoloaded package in a running emacs session and it breaks emacs the next time you start it. And sometimes autoloading itself is unwanted sitting and staring and waiting time. (The package initialization section at http://emacswiki.org/emacs/ELPA is illustrative.) Most of these issues can be overcome, but the whole situation is messier than I’d like.
What’s more, there’s a very simple solution, and since I’ve discovered it, everything has just gotten a lot simpler. It’s not a package or a plugin or a software solution at all. It’s an attitude shift, and it struck me like a lightening bolt.
I DON’T CARE HOW LONG IT TAKE EMACS TO START UP.
What’s that?
I DON’T CARE HOW LONG IT TAKE EMACS TO START UP.
Yeah, ok, vim starts up like that! But here’s the thing. I don’t use emacs like I use vim (which I do sometimes). I start emacs up on my local machine maybe a couple times a week, and leave the session running for days. So even if it took 30 seconds to load (it takes 3.5), that’d be, like, a minute a week. I’ve already made that time up.
This is a pretty mundane realization, perhaps, but I no longer have to care about where and when things get loaded, about configuring things in the right place and time, about autoloads and eval-on-load
s and related concerns. I just throw in a require
and I get on with my life.
Getting On
So already in an iconoclastic state of mind, I was well positioned to tackle issue 2: Where to put things?
See, some things were in bindings.el
, others in defuns.el
, settings.el
, [package-name-here]-config.el
, and on and on and on. Where do I put hooks that depend on custom defuns? Bindings for plugins? Third-party package configs vs. built-in package configs? What’s worse, directories were nested three, four levels deep, and if I had to find something, I’d be acking
all up in my .emacs.d
 and it was a mess. And I bet you do the same thing.
This was causing me pain, and then I remembered an incident.
The Incident
I was working on an iOS project, and it had a ton of files, and I was working in Xcode, and I hated Xcode, but needed the smart completion, which is a weak area for emacs. But I downloaded Code Pilot, and that made things a little better. And I used CTRL-6
to navigate to methods in a class (think imenu), and used project search a lot, and I started not caring about where things were, because as long as I new part of the name, I was there.
It took some work to get there in Xcode, but this is the sort of setup I strive for, whatever I’m developering, this is the reason I emacs, and this is key to productivity and happiness for my money.
My colleagues in the meantime where busy nesting directories and arranging things just so and painstakingly putting files in alphabetical order because seemingly Xcode won’t do that for you. And not only did they spend a lot of time and energy worrying about where to put things, they had to think a lot when they wanted to find them again.
For me, one of the biggest enemies of flow, and more generally, productivity, is the need to think about a thing that is not the thing you’re thinking about to solve the problem at hand. So if a particular task calls for jumping around to several files, any time and effort spent thinking about where some file is, or even just going through the mechanics of navigating directories, is likely to dissipate my fragile flow, kill my productivity, raise my hackles, and generally make me grumpier.
In It
So that’s the incident I remembered, and I realized that if you have good navigation support, it doesn’t matter where you put things. And if it doesn’t matter where you put things, why not take the simplest, stupidist, most obvious approach? The approach you probably took the first time you wrote a bit of emacs configuration code.
So where to put things? How about init.el
? What things?
ALL THE THINGS!
I was initially incredulous of my own bat-shit idea, but I went through with it anyway. And it has been liberating.
You see, when everything you care about is in one place, you no longer have to think about where you put stuff. Where are my bindings? init.el
. Hooks? init.el
. Configuration for package X, Y, and most especially Z? init.el
. Then fold in tools like occur
and isearch
and imenu
and then do something like:
[lisp gutter=”false”]
(defun imenu-elisp-sections ()
(setq imenu-prev-index-position-function nil)
(add-to-list ‘imenu-generic-expression ‘("Sections" "^;;;; \\(.+\\)$" 1) t))
(add-hook ’emacs-lisp-mode-hook ‘imenu-elisp-sections)
[/lisp]
Which provides rough structure to the init file with section headings that look like
[lisp gutter=”false”]
;;;; BINDINGS
…
;;;; HOOKS
…
[/lisp]
So when I imenu
(using helm-imenu
in my case), I get this:
And boy that’s nice.
How about this:
[lisp gutter=”false”]
(defun init-imenu (p)
(interactive "P")
(find-file-existing "~/.emacs.d/init.el")
(widen)
(helm-imenu)
(if p (init-narrow-to-section)))
[/lisp]
Bind that globally, and any time you get the urge to change your emacs config, you can immediately jump to the desired part of your config, by section or by function. init-narrow-to-secion
is just:
[lisp gutter=”false”]
(defun init-narrow-to-section ()
(interactive)
(save-excursion
(beginning-of-line)
(unless (looking-at "^;;;;")
(re-search-backward "^;;;;" nil t))
(push-mark)
(next-line)
(re-search-forward "^;;;;" nil t)
(previous-line)
(narrow-to-region (region-beginning) (region-end))))
[/lisp]
Which is sometimes nice.
The downside is that dependency is now handled by order of code (from top to bottom) rather than the more robust require
system. But it’s a small downside, which hasn’t caused me much pain, and one that can be remedied as needed by pulling any of your more library-ish stuff into a separate file and requiring it when desired.
(Probably obvious, but I’m talking about settings and hooks and bindings and one-off functions here. My plugins are not inlined in my init.el
. That would be craaazy.)
You?
I challenge you to challenge yourself to consider why you care about emacs startup time. Is it the same reason I cared? Because all the emacs literature told you you cared?
Or perhaps someone out there has a head-slappingly simple system for auto-loading and configuring everything with no added complexity, in which case, I’m very curious to hear about it.
I’m also interested how folks with non-trivial emacs configurations are organizing them. Or if you adopt the one-big-ass-init-file approach, I’m curious to hear how your milage varies.