Emacs Config: You’re Doing It Wrong

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-loads 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!
x-all-the-things-template

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:

(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)

Which provides rough structure to the init file with section headings that look like

;;;; BINDINGS
...
;;;; HOOKS
...

So when I imenu (using helm-imenu in my case), I get this:

Screen Shot 2013-01-19 at 2.40.52 PM

And boy that’s nice.

How about this:

(defun init-imenu (p)
  (interactive "P")
  (find-file-existing "~/.emacs.d/init.el")
  (widen)
  (helm-imenu)
  (if p (init-narrow-to-section)))

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:

(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))))

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.

About selah

Programmer at Prominence Advisors. Also dabbling musician. Also cycling commuter.
This entry was posted in Emacs, Programming and tagged . Bookmark the permalink.

8 Responses to Emacs Config: You’re Doing It Wrong

  1. user says:

    “We emacsians spend a good bit of time biting our nails over our favorite editor’s startup time, ”

    Do not generalize from your own experience. I leave Emacs running for weeks, I do not have to restart it, so startup is simply not an issue for me and there are lots of emacs users who use emacs like this, so I would change the “We emacsians” part to “Some emacsians” which is closer to the truth.

  2. Will says:

    I agree with the previous post. I never care of about the start-up time of emacs, since it starts with my machine in server mode and I will only ever call emacsclient (which takes no time).

  3. Will says:

    s/of about/about/

  4. Pingback: Another Take on Emacs Configuration | Irreal

  5. Benaiah says:

    One thing that really helped me with my configuration woes was migrating to using org-mode with babel for my init script. This lets you document your config in-place much more powerfully than just using comments. If you don’t feel like setting it up, you could try emacs-starter-kit, which gives you a very basic configuration and an org-mode setup.

  6. Dima says:

    In vim I used folds for sections and subsections instead of using similar to helm plugin. All folds were closed by default when I opened to file. I wonder if something like this could be done here as well. I guess helm integration is nice as well so I can just reuse same marks for folds as well.

  7. selah says:

    This can be roughly achieved in Emacs is using an org-mode based configuration like Eric Schulte’s literate config: https://github.com/eschulte/emacs24-starter-kit.

    Personally, I prefer quick intra-file navigation functionality to code folding, but if that’s your thing, the link above could get you there.

  8. Hi there, just became aware of your blog through Google, and found that it’s truly informative.
    I’m gonna watch out for brussels. I will be grateful if you
    continue this in future. Many people will be benefited from your writing.

    Cheers!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">