Monday, December 03, 2012

Variable customization

There are a few major kinds of customization you can perform:
  1. Change the variables for already existing code.
  2. Create new keybindings (or erase ones that were there already).
  3. Create new functions to add new interactive capabilities.
  4. Load new packages that alter the behavior of emacs, and call functions that setup the initial state of those packages.
I'm going to focus on changing the variables in this post.

There are two main ways to change variables: by using setq directly, or by using customize. Looking at my init file, my first line setting a variable directly is this:

;; This only gets set for real when cc mode is enabled
(setq c-buffer-is-cc-mode nil)

This is an example of using setq. If you are new to elisp, you may wonder why it is called setq instead of set. The answer is pretty simple: setq stands for "set quoted", and there already is a set. Here's the same thing with set:

(set 'c-buffer-is-cc-mode nil)

The difference here is we have to quote the variable we are setting, c-buffer-if-cc-mode, whereas in setq it is done for you. At any rate, setq is more idiomatic in elisp, so let's use that.

The other main way to set variables in using customize. To set the same variable with customize, we can type M-x customize-variable and then enter c-buffer-is-cc-mode. However, if you try this, you'll see that c-buffer-is-cc-mode doesn't work; it has no customization option. That's probably because it isn't intended to be customized, and indeed if you look at it, you'll see that it is buffer-local, which means that it isn't a global variable, it is one that has a potentially different meaning in every buffer. To such variables, there isn't much need for global customization.

As you can see, you can't customize everything. For many things, though, customization is pretty nice. You can also do M-x customize-group and then put the name of some package, such as ido. You'll see a list of variables printed as phrases: the first letter capitalized and the dashes replaced with spaces. For example, the variable ido-before-fallback-functions is referred to as "Ido Before Fallback Functions". For variables with a freeform value, you can edit the value in a small textbox. For lists, you can insert an element at a time.  For things that can be one of a few values, you get a selection widget.  It's all very nice and easy to use.  The documentation is also right there. After making a modification, you can save for the current emacs session, or permanently. Permanently saving puts the customization in your initialization file in a stanza that looks like:

(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(ido-auto-merge-delay-time 1.5))

There is a choice to make for customizing options, then. Use plain-old setq, or use the snazzy built-in customization?  I think each has it's uses, but my current configuration isn't a good example of this. Let's fix it.

Generally, I think it's best to use customization for machine-specific things, and setq for everything else. setq has some nice advantages to it, but also a few drawbacks. The advantages are that you can group the setq statements with similar customization (for example, keybindings). Also, you can comment on the variable settings. This is very important. Looking at my setting of c-buffer-is-cc-mode, my only comment is "This only gets set for real when cc mode is enabled". This comment sucks. Looking at this, I don't recall why I felt the need to set it, and this comment does not help at all. Ideally, I'd like to know what horrible fate will befall me if I fail to set this variable. I'm going to remove this from my config. When I figure out why I needed it, I should remember to leave a better comment. Maybe I'll never figure it out, which is fine, since my config will be just that much cleaner.

The drawbacks to setq are that the UI for changing the settings is not nearly so nice as the built-in customize. That's about it.

A variable can be set with setq before the variable is even defined, and the value set with setq will still take effect. For example:

(setq foo 10)
(defvar foo 3 "An example variable")
(princ foo)  

--
10

It's also worth noting that you can have one setq to set many variables, like so:

(setq foo 1
      bar 2)

As I mentioned above, customization may be useful for per-machine settings, such as the location of binaries. This is because we can separate out the customization to another file. The docs to custom-file explain this nicely, but the basic idea is that you setup another file to hold your customization. With things split into another file, you can add this file to your .gitignore, and then just not check it in. There should be no need to.

To do this, first, copy any customizations we have into your new file. If you have an ~/.emacs.d/ directory like I suggest, then ~/.emacs.d/custom.el is a reasonable choice.

(setq custom-file "~/.emacs.d/custom.el")
(load custom-file t t)

Loading in this way will let the file not be there without throwing an error.  That's fine, usually it shouldn't be there when running this config on a new machine.

Just in case you were wondering, customization does not act like setq; it matters where you do it:

(custom-set-variables
 '(bar 10))
(defvar bar 3 "An example variable")
(princ bar)

--
3

This is why customization always happens last.

You can actually change this behavior. And you can add comments.

(custom-set-variables
 '(baz 10 t nil "With the t argument, this will be evaluated explicitly"))
(defvar baz 3 "An example variable")
(princ baz)

--
10

There is one other interesting thing you can do with custom-set-variables, which is to have the customization per-feature.

(custom-set-variables
 '(jabber-keepalive-interval 10 nil (jabber) "Only pertains to feature 'jabber"))

This will load up the feature in question, in this case jabber when customizing. I'm not really sure why this is useful. If anyone knows, please tell me in the comments.

Customization has even more complexity. You can have customization themes that you can change between, or layer on top of each other. It's all very interesting, but to me it's best to just have the separate file, and store machine-specific settings there.

In summary, my suggestions for variable settings are:
  1. Use setq for each variable you want to set.
  2. Unless it is dead obvious, comment on why you are changing the variable.
  3. Keep all similar variables together.
  4. If the variable is something inherintly machine-specific, such as the location of a binary, then customize it, and put your customization in a different file that will not be shared between computers.
  5. Load customization last.
I plan on going through my configuration file and making these changes, and I encourage you to do the same.

2 comments:

Hunter Freyer said...

One thing I'm not totally sure how to do is customizations that happen when you enter a mode. For instance, by default I want whitespace-mode to highlight tabs, but not when I'm writing go. whitespace-mode has a variable called "whitespace-style" which is a list of things for it to highlight. How would I tell emacs to remove "tabs" from that list when I enter go-mode? Currently this is what I have:

(add-hook 'go-mode-hook
(lambda ()
(set (make-local-variable 'whitespace-style)
'(face trailing space-before-tab space-after-tab empty))
(whitespace-mode)))

But constructing this was entirely guess-and-check until it worked, and it's not ideal because I explicitly set whitespace-mode, rather than just removing "tabs" from my default config.

Andrew Hyatt said...

Customizing on entering a mode like you have done is correct. I should go into this in another post.

As far as removing tabs from 'whitespace-style, you could have done

(set (make-local-variable 'whitespace-style) (remove 'tabs whitespace-style))