Emacs: Tab Width Woes
Emacs uses the value of the tab-width
variable to decide how wide
tab characters should be displayed. Setting tab-width
is usually
one of the first customizations Emacs newbies make:
(setq-default tab-width 2)
Displaying tab characters at whatever width you prefer works well for most applications but eventually fails when files mix tab and space characters and (invariably) make assumptions about what the width of those characters will be.
One particularly conspicuous offender is the Emacs (Lisp) source code.
I feel like I’ve recently reached a new level of skill with Emacs–a
point where I’m finally effective enough with it that I write Emacs
Lisp to improve my workflow as a regular part of my daily workflow.
One consequence of this is that I spend a lot of time reading Emacs
source code, looking for either implementation clues or undocumented
gems. It’s a hell of a lot of fun but unfortunately it means that I
spend a lot of time alternating between my preferred tab-width
of 2
and 8, the size the Emacs source thinks a tab should be.
My initial solution was to throw together a quick function to toggle
between these two values, which I bound to C-c TAB
:
(defun camdez/toggle-tab-width ()
"Toggles `tab-width' between 8 and 2."
(interactive)
(setq tab-width
(if (= tab-width 2)
8
2))
(message "Tab width set to %d" tab-width))
It could definitely use a lot of improvement but that’s the kind of
thing that I now tend to hammer out quickly and improve later if the
concept survives the prototyping phase. I keep this type of hacks in
a file called experimental.el
which I load from my Emacs
initialization file. I also keep this file in the x
register so that
I can immediately jump to it with C-x r j x
:
(load-library "experimental")
(set-register ?x (cons 'file "~/.emacs.d/experimental.el"))
But I was never thrilled about the idea of having to manually toggle
that value, even if it was a couple keystrokes away. I started
scheming about ways to analyze source files to determine what
tab-width
they implied (certainly possible–not a lot of fun) when
I realized that the Emacs source was really the only place where I was
having this problem.
My next thought, then, was to create a hook that would fire whenever a
file was opened, examine the file path, and set the tab-width
to 8
if it was part of the Emacs distribution. But hardcoding a file path?
That’s a bit distasteful.
I realized today that there’s a wonderfully effective, simple solution
called “Directory Variables”. All you have to do is put a file called
dir-locals.el
in a directory and whenever Emacs visits a file in
that directory (or one of it’s subdirectories), those local variables
will be loaded and applied. In fact, there’s even a command that will
build the file contents for us. Here’s how we can set it up for the
Emacs source:
- Open a file in the Emacs lisp directory. The easiest way to this is
pull up the documentation for a function in the standard distribution
(say,
occur
) withC-h f occur RET
, tab to the file name it says it’s defined in, and hitRET
to jump to that function’s definition. - Next, run
M-x add-dir-local-variable RET
. - Hit
RET
to accept the default of only applying this variable toemacs-lisp-mode
. - Enter
tab-width
as the variable to set. - Enter
8
as the value of the variable. - You’ll now be looking at a new file with contents like this:
;;; Directory Local Variables
;;; See Info node `(emacs) Directory Variables' for more information.
((emacs-lisp-mode
(tab-width . 8)))
Save the file with C-x C-s
and you’re good to go. Now whenever you
open a file in the Emacs source code it’ll have the tab-width
properly set to 8.