Emacs: Organize Init File

By Xah Lee. Date: . Last updated: .

This page is a guide on organizing your emacs init file.

Many emacs users, have hundreds of lines in their emacs init file, accumulated over the years. Large emacs init file makes emacs start slow, and is a problem when you upgrade emacs.

When you find some elisp code on the web, you pile it in your emacs init and you can immediately go back to work on things you need done. That is the beauty of it. The best way i find in keeping emacs init organized, is just to break them into multiple files.

Split Init File into Multiple Files

My structure is like this

The sole content of this file is:

(load "~/git/xah_emacs_init/xah_emacs_init.el")

Then i have

The xah_emacs_init.el basically just contain lines to load other files.

The xah_emacs_settings.el settings file is all the essential settings and does not need installing extra packages.

If i'm on a new machine, i just use the settings file to begin with. (For different machines setup, i prefer case-by-case per machine manual setup basis.)

The content of xah_emacs_init.el looks like this:

(require 'package)

;; more stuff

(defun xah-get-fullpath (@file-relative-path)
  "Return the full path of *file-relative-path, relative to caller's file location.

Example: If you have this line
 (xah-get-fullpath \"../xx.el\")
in the file at
then the return value is
Regardless how or where emacs_lib.el is called.

This function solves 2 problems.

(1) If you have file A, that calls the `load' on a file at B, and B calls `load' on file C using a relative path, then Emacs will complain about unable to find C. Because, emacs does not switch current directory with `load'.

To solve this problem, when your code only knows the relative path of another file C, you can use the variable `load-file-name' to get the current file's full path, then use that with the relative path to get a full path of the file you are interested.

(2) To know the current file's full path, emacs has 2 ways: `load-file-name' and `buffer-file-name'. If the file is loaded by `load', then `load-file-name' works but `buffer-file-name' doesn't. If the file is called by `eval-buffer', then `load-file-name' is nil. You want to be able to get the current file's full path regardless the file is run by `load' or interactively by `eval-buffer'."

  (concat (file-name-directory (or load-file-name buffer-file-name)) @file-relative-path)

;; --------------------------------------------------
;; load the plain emacs settings

(load (xah-get-fullpath "xah_emacs_settings"))

;; --------------------------------------------------
;; load packages

(add-to-list 'load-path "~/git/xah-fly-keys/")
(require 'xah-fly-keys)

(add-to-list 'load-path "~/git/xah-find/")
(require 'xah-find)

;; more here

;; --------------------------------------------------
;; load files

(load (xah-get-fullpath "xah_emacs_abbr"))
(load (xah-get-fullpath "xah_emacs_keybinding"))
(load (xah-get-fullpath "xah_emacs_keybinding_mode_specific"))
(load (xah-get-fullpath "xah_emacs_settings_external_packages"))
(load (xah-get-fullpath "xah_emacs_file_association"))
(load (xah-get-fullpath "xah_emacs_html"))
;; more here
(load (xah-get-fullpath "xah_emacs_misc"))

It loads other init files such as:

The misc.el is catch all. Anything misc or temp or worry-later i dump there.

When next time you have more code you want to add, just pick a file and add there.

The advantage of separate files is that you can selectively add or comment out which one to load. (as opposed to comment out blocks of code in a big file, which is less easy to manage)