Learn Emacs Lisp while customizing Emacs

Table of Contents

Work in progress. Questions/ideas? sacha@sachachua.com

Available at http://sach.ac/baby-steps-elisp
Github: https://github.com/sachac/emacs-notes . Patches welcome. =)

While I'm drafting this, you may want to check out the following resources:

UPDATE: Go check out http://pages.sachachua.com/emacs-notes/how-to-read-emacs-lisp.html instead.

Previous draft follows below.

Why learn Emacs Lisp?

Emacs is amazingly customizable. If you want to tweak Emacs to support the way that you'd like to work, learning Emacs Lisp will definitely pay off. People have used Emacs Lisp to turn Emacs into a musical instrument, a TODO manager, a webserver, and all sorts of other things.

You don't need to be a programmer in order to learn and use Emacs Lisp. Even though you'll technically be programming in it, just think of it as configuring Emacs. This guide will start you off with simple customizations, and then you can go from there. In this guide, you'll be changing options much like you might change them through other applications' Options dialog. It's just that in Emacs, it's easier to do most things in text.

In addition to reading this guide, you may want to check out Planet Emacsen and Emacs Wiki for plenty of inspiration. Seeing what other people have done with Emacs can nudge you to explore more of the Emacs Lisp code that makes it possible.

I'm going to assume that you've already installed Emacs 24 or later and that you're familiar with basic editing. If you haven't gone through the tutorial yet, you can click on the tutorial link that appears when Emacs starts up, or you can use Ctrl-h t (help-with-tutorial). All right. Let's start!

How to run Emacs Lisp code

The first thing you need to learn is how to run Emacs Lisp. By default, Emacs starts up with a *scratch* buffer that's in Emacs Lisp mode. You can use this to try out code temporarily without saving it to your config. Type or paste in the code that you want to try, then use M-x eval-buffer to evaluate all the code in that buffer.

If you want to run some of the code (but not all the code), you can select the region and then type M-x eval-region.

Emacs Lisp code is made up of expressions that are enclosed in sets of parentheses. To run just one expression, put your cursor right after the last parenthesis (but not on the parenthesis itself). Type C-x C-e (eval-last-sexp). You should see the results that expression.

Let's try that with some examples. Type or copy the following into your *scratch* buffer and use M-x eval-buffer. It should show "Hello world" in the echo area near the bottom of your Emacs frame.

(message "Hello world")

Try M-x eval-region and C-x C-e (eval-last-sexp) as well.

There are a few other ways to quickly evaluate Emacs Lisp code. If you just want to quickly evaluate something, try M-: (eval-expression). M-x ielm opens an interactive buffer where you can type code and then press RET (Enter). When you're starting out, you'll probably want to be able to easily read and modify the code, so I suggest using M-x eval-buffer, M-x eval-region, or C-x C-e (eval-last-sexp) instead. If you've downloaded emacs-lisp-tutorial.org and are reading this in Emacs, you can evaluate code by putting your text cursor on the #+begin_src emacs-lisp line and typing C-c C-c. Make sure to re-evaluate it if you make any changes. Might be handy.

Since the *scratch* buffer is not automatically saved to a file, you may want to put your code into a file instead. You can create any file with the .el extension (ex: test.el) and Emacs will switch to Emacs Lisp mode when you open it.

How to save your configuration

When you like something and you want to make it part of your configuration, open the file ~/.emacs.d/init.el. If you started with an older version of Emacs and have a ~/.emacs file instead, be sure to move its contents to ~/.emacs.d/init.el and remove the ~/.emacs file.

It's usually safe to add your new code to the bottom of the file. If your init.el already contains some code, make sure you add your new code after the last closing parenthesis. If you're changing things like load-path, you may want to put the code earlier in the file (before the library is loaded). Save your init.el and restart Emacs.

What if you added something to your init.el that caused an error when you started Emacs? Don't worry. You can find out which part caused the error with emacs --debug-init. You can also start Emacs without your configuration file by typing emacs -q at the command-line. Then you can open your init.el, remove the problematic line, and restart to see if that fixed it.

Okay, let's start writing Emacs Lisp!

How to load modes to change the way that Emacs behaves

Emacs has "modes" that load different pieces of functionality. You've already seen how Emacs Lisp Mode (or emacs-lisp-mode, which is the actual name in Emacs Lisp) changes the color of code in the *scratch* buffer. Other modes affect how you use Emacs. Here's an example of a mode that you may find useful. It turns on completion for M-x.


Put that in your *scratch* and use M-x eval-buffer to evaluate it. Now try M-x again, and start typing eval-buffer. As you type, Emacs will suggest possible completions. There are even more shortcuts for M-x, but this is probably a good start for you.

Here are some other modes that might be helpful:


Try using C-x C-e (eval-last-sexp) to evaluate these lines one at a time. Put your cursor after the ending ) on each line, then type C-x C-e.

icomplete-mode and the other modes are all examples of functions. The first "word" after the opening parenthesis of an Emacs Lisp expression is usually a function. You can learn more about functions with F1 f (describe-function). There are tons of functions in Emacs (and even more once you start installing and loading packages). You can discover useful functions by browsing through other people's configuration, looking at the source code of other functions you're interested in, checking out EmacsWiki, asking on mailing lists and newsgroups, or using the M-x apropos-command function.

How to specify options when you're calling functions

Some functions need more information. For example, prefer-coding-system is a useful function if you work with files that have special characters or different languages. The following Emacs Lisp code sets the default to UTF-8, which is a common standard.

(prefer-coding-system 'utf-8)

Here's another example. Let's say that you want to open a file whenever you start Emacs. The function to open a file is called find-file, and you can give it the name of the file to open. The code below switches to a buffer visiting file "todo.org" in your home directory. If the buffer doesn't exist, it will be created. If the file doesn't exist, it will be created automatically when the buffer is saved - for example, when you press C-x C-s (save-buffer) in that buffer.

(find-file "~/todo.org")

(See A Baby Steps Guide to Managing Your Tasks with Org for more info on Org files.)

Here's a third example, for when you get tired of typing "yes" or "no" to confirmation prompts:

(fset 'yes-or-no-p 'y-or-n-p)

fset is a function that replaces a function (yes-or-no-p) with another function (y-or-n-p). Sounds complicated? A little. You can use the snippet right now without digging into how it works, and you'll learn more about functions over time.

How to set simple variables

In addition to calling functions, you can also set values that affect what code does. Many functions check the value of different variables. For example, this tells Emacs to keep old versions.

(setq delete-old-versions -1)

setq means "Set this quoted variable to this value." The quoted part just means that the first argument (in this case, delete-old-versions) is treated as a variable name instead of being used to look up a different variable to set. The code is the same as the code below:

(set (quote delete-old-versions) -1)

… but the setq version is shorter, so everyone uses that instead. This is also why you should be careful to type setq instead of set, as set refers to a different function. If you accidentally use set instead, you'll usually see something like a Wrong type argument: symbolp, value

To see the documentation for a variable, use F1 v (describe-variable) and type in the variable name. The variable name is the first argument to setq, so you would type in F1 f delete-old-versions.

You can customize many variables through the M-x customize interface, but you'll most often see people sharing their configuration with Emacs Lisp code instead. It can be shorter to say "Add this code to your init.el" than it is to say "Use M-x customize-variable to change the user-email-address value", especially if there are lots of options to change. Not everything can be changed through the Customize interface, too. Emacs Lisp is more powerful, and it's easier to read afterwards. I tend to use M-x customize to explore, but I use Emacs Lisp code to set things up.

Like the way there are tons of functions, there are also tons of variables, and you can discover interesting variables using the same techniques: reading other people's code, exploring, etc.

How to set buffer-local variables

Some variables are buffer-local. This means that the value in one buffer (or file) is separate from the values in other buffers. To find out if a variable is local to a buffer, use F1 v (describe-variable) to look at the documentation for that variable. For example, the documentation for tab-width has "Automatically becomes buffer-local when set".

You can set the default value of a buffer-local variable with setq-default. The following code sets the tab width to 2 by default:

(setq-default tab-width 2)

How to set more complex variables

Some variables contain lists of data. Here's an example:

(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))

This is one of the things people usually want to change right away. By default, Emacs saves backup files in the current directory. These are the files ending in ~ that are cluttering up your directory lists. This code stashes them in ~/.emacs.d/backups, where you can find them with C-x C-f (find-file) when you need to.

alist stands for association list. Each entry in the list is enclosed in (…). The first part of the list is the key that is used to look it up, like the way a dictionary has words. The second part of the list is the value that Emacs uses, like the definitions in a dictionary. If you're curious, the Emacs Lisp reference manual has more details.

How to load libraries

Emacs has a lot of libraries. To save memory and to simplify operation, not all of them are loaded at startup. In addition to the libraries that are built into Emacs, you can also download packages and Emacs Lisp files, save them to a directory that Emacs can find, and load them.

Here is one way to load the code from an Emacs Lisp file.

(load-file "~/.emacs.secrets")

If the file doesn't exist, you'll get an error. You can check if it exists with:

(if (file-exists-p "~/.emacs.secrets")
    (load-file "~/.emacs.secrets"))

If you use your Emacs configuration on multiple systems, you might want to load some configuration based on the name of the system you're on. For example:

(if (file-exists-p (concat "~/.emacs.d/" (system-name) ".el"))
    (load-file (concat "~/.emacs.d/" (system-name) ".el")))

To find out which file the code is looking for, you can put your point after the closing ) for in .el") (so it's on the second parentheses) and then type C-x C-e (eval-last-sexp). That will display the result of the expression in your echo area.

load-file loads a specified file if you know its path. For libraries, though, it's easier to use require. Here's an example that loads the eldoc library and turns on the mode. Eldoc displays information about Emacs Lisp functions or variables in the minibuffer when you move your point.

(require 'eldoc)

require takes the name of the feature to load. Here, it's ='eldoc=. the quotation mark before it means that it should be treated as a literal symbol, not as a variable to look at for a value. If this features has already been loaded, require does nothing. If not, require searches through the list of directories in load-path for an Emacs Lisp file with that name and which provides that feature.

If you've downloaded the Emacs Lisp file or package on your own, though, you'll want to add its directory to the load path. See How to add to lists for details.

How to add to lists

add-to-list is a straightforward way to add an item to a list. Here's an example of adding something to load-path, which controls where Emacs looks for packages or features to load:

(add-to-list 'load-path "~/elisp")

If you want to add to the end of the list, you can specify t as the optional APPEND argument, which is the third argument in the list.

(add-to-list 'load-path "~/elisp/sample-mode" t)

add-to-list adds an item only if it doesn't already exist. The list must already be defined. If you try to add an item to a list that does not exist yet, you'll get the following error: Symbol's value as variable is void: listname. Make sure you load the feature before you add to any lists defined in that feature.

If you want to always add something to the beginning of the list, you can delete it from the list and then add it back.

(setq load-path (delete "~/elisp" load-path))
(add-to-list 'load-path "~/elisp"))

If you don't mind duplicates (or you want them), you can use cons instead. cons constructs a cons cell, which is the data structure that makes up lists. For more information about cons, see the Emacs Lisp Intro section on car, cdr, cons: Fundamental Functions.

(setq load-path (cons "~/elisp" load-path))

How to add package archives - to be written

How to add hooks - to be written

How to set up your own keyboard shortcuts

If you find that you regularly need to run one or more commands that don't have a default key binding set up for them, it's time to start defining your own key bindings.

Each key binding you define belongs to a specific keymap.

If you want a binding to be available globally, irrespective of the mode you are in, you can add it to the global-map like this:

(global-set-key (kbd "M-s r b") 'revert-buffer)

Here, we are creating a global key binding (M-s r b) for the revert-buffer function.

You can also override an existing key binding this way. For instance, the function list-buffers (which brings up a list of all buffers that are currently open) is bound to C-x C-b by default. If you prefer ibuffer for listing and operating on open buffers but have already built up muscle memory for the C-x C-b binding, you can rebind it like this:

(global-set-key (kbd "C-x C-b") 'ibuffer)

You can also set up keybindings to work only in specific modes. This is done with the help of the define-key function:

(define-key org-mode-map (kbd "C-c a") 'org-agenda)

Here, we are telling Emacs to create a key binding for the org-agenda command that will only work in org-mode buffers.

It is important to note that mode-specific key bindings will override global bindings. So unless you are explicitly trying to override an existing binding (as described above), before setting up a new binding it is always a good idea to check if the key sequence you are thinking of is already bound to a command. (This can sometimes be a great way of discovering functionality you hadn't been aware of!) To check whether a binding is currently in use in any of the modes that are enabled for the current buffer, type C-h k (describe-key) followed by the binding. If it isn't, you will see a message in the echo area telling you that the binding is undefined. If it is bound to a command, Emacs will bring up a *Help* window with documentation for the command.

It can sometimes be desirable to unset specific key bindings. For global bindings, you can use the global-unset-key function:

(global-unset-key (kbd "M-g M-g"))

Mode-specific bindings can be disabled by setting them to nil:

(define-key magit-mode-map (kbd "M-s") nil)

If you define lots of custom keybindings, you may want to check out John Wiegley's bind-key package. If you install bind-key and use it to define keybindings, you can then use M-x describe-personal-keybindings to see which keybindings Here are some examples for bind-key:

      (require 'bind-key)
(bind-key "C-c a" 'org-agenda)
(bind-key "C-+" 'text-scale-increase)
(bind-key "C--" 'text-scale-decrease)
;; You can use bind-key to modify keymaps
(bind-key "i" 'org-agenda-clock-in org-agenda-mode-map)

How to define your own functions - to be written

How to prompt for information - to be written

How to change existing functions with advice - to be written

(c) 2014 Sacha Chua - Creative Commons Attribution License (feel free to use, share, remix)


Thanks to:

  • itsjeyd for the section on keyboard shortcuts
Back to top | E-mail me