Speak with an Emacs Lisp
Table of Contents
1 The Basics
1.1 Intro
1.1.1 Why Learn Emacs?
There's been a lot of jibber-jabber on the net lately about Emacs. And maybe you know that one guy who won't stop preaching its gospel. Or maybe you're just sick of being a n00b who edits code in Notepad. Isn't it time you learned what all the fuss is about?
Recently at a Clojure conference I was suprised by the lack of people using Emacs. A casual survey other's computer screens showed barely a third of people using it. Surely, if they knew Emacs, I thought, they'd use it instead. Why wouldn't Lisp programmers want an editor that could be customized to do almost anything in Lisp?
Because it's hard to learn. And even harder to get started learning. Emacs is meant to be customized, which is good because its default settings are horrible. You need to do a lot of customization to get it working nicely, which entails writing Emacs Lisp, which (surprise!) is hard to do if you don't know Emacs Lisp.
And that's the problem with Emacs. There are a lot of good packages that provide "sane" default settings, with the intent of making it easier for beginners to get started with Emacs without having to write any Emacs Lisp. But what do you do when things stop working? How can you debug Emacs Lisp if you don't understand the language?
We're up to our ears in books about nearly every programming language under the sun, and there are several books published in the last few years about other editors such as vim. But Emacs just sits in the corner, getting no love, with just a couple books, published in the 90's, about versions so old they're barely relevant. There are other resources online for learning Emacs, including many good intoductory tutorials, but to really get the most out of Emacs you'll want to learn more than just the basics.
Hence this book. We'll learn Emacs Lisp by first using to fix all those nasty default settings, and then by customizing Emacs to do crazy shit. By the end, you'll know enough that you'll be able to turn it into your perfect editor. If you're reading this book, hopefully you don't need me to sell Emacs to you, but I just did. That's the elevator pitch: if you learn Emacs, you'll be able to make it work however you want and do almost anything.
You should definitely have past programming experience before reading this book. Another Lisp language like Clojure will make things a lot easier for you, but isn't necessary nor assumed.
I've seen too many people give up on Emacs after a few weeks because of one minor quirk or another that they had no idea how to fix. This book is dedicated to those people.
1.1.2 Installing
Before we go any further, you'll need to install Emacs. Hopefully you already know how to install things and don't need me wasting my breath, but make sure you have version 24.5 or newer with full GUI support (unlike VIM, most Emacs users don't run Emacs inside a terminal). Beware that OS X and many Linux distributions come with an ancient version of Emacs installed. Check the output of emacs --version
from your command line to make sure you have the latest version.
- Mac OS X
I recommend installing Emacs via Homebrew. Hopefully, you already have Homebrew installed, but if not, check out http://brew.sh/ for instructions on how to install it. After that, run
brew update # update package info brew install emacs --cocoa # install Emacs with Cocoa GUI brew link emacs # set up symlinks to Homebrew version of Emacs brew linkapps # create Emacs.app in /Applications
Another good option is installing a pre-built binary from http://emacsformacosx.com/.
- Windows
Head over to https://www.gnu.org/software/emacs/#Obtaining and follow the links to find a nearby GNU mirror. Look in the
windows/
folder of the mirror's file listing and download the appropriate ZIP file. - Linux
If you're running Linux, you know how to install packages. Again, just make sure you have an up-to-date version.
1.2 Core Concepts
If you're interested in (better) learning Emacs Lisp, you probably have at least a vague familiarity with Emacs itself. If so, feel free to skim over this chapter or skip it altogether. Otherwise, we'll quickly go over some fundamental Emacs concepts.
1.2.1 Frames & Windows
Let's jump right into it. Fire up Emacs and we'll get started. You should see a screen like this:
Figure 1: Emacs with Welcome Splashscreen
It's important to remember Emacs started its long evolution in the seventies, before GUIs became wide-spread. Emacs predates many of the standard terms used to describe computer interfaces today. And so Emacs uses some of these terms in a different, sometime counterintuitive way.
What we'd think of today as an application window is called a frame in Emacs. A window, on the other hand, is a subdivision of a frame. The picture above is of a single frame with a single normal window, displaying a slashscreen with a neat 90's-era Emacs logo, and a minibuffer window, which is the single line of text at the bottom of the frame, used for displaying messages or prompting for input. A frame can only have a single minibuffer window1, but it can have many regular windows. The minibuffer window is a special case, and you can safely ignore it for much of our discussion of windows going forward.
The "current" window is said to be selected, as is the current frame; switiching to a different window or frame is known as selecting it.
1.2.2 Buffers & Visting Files
Here's a (customized) frame with a window with a Dired2 buffer, and another with a buffer that's visiting an Emacs Lisp file.
Visiting a file is Emacs-speak for opening it. A buffer contains text to be edited, and other context you can access through various variables or functions. That text might be the contents of the file it's visiting, although not all buffers are neccessarily visiting files: their content can be generated programatically. Each window displays a single buffer at a time; a buffer can be displayed in any number of windows – one, several, or even none.
One buffer is always said to be current. Typing (usually) inserts text in the current buffer; most editing commands will affect it. The current buffer is often the buffer currently displayed by the selected window, but not always; Emacs Lisp code can switch to a differenty buffer (usually temporarily), in order to make changes to it.
1.2.3 Modes & Keymaps
Every buffer has a single major mode that fills a specific purpose, such as editing code in a certain language or EDiting DIRectories. A major mode can defines everything from syntax highlighting and indentation rules to special commands specific to that major-mode.
Keymaps bind events such as keystrokes or mouse actions to specific commands. Global keybindings, available throughout Emacs, are defined in the global-map
. Major modes usually define a keymap of their own; this keymap will only be active when the current buffer has that major mode. Keybindings in major mode keymaps are used in preference to ones in the global keymap in case of a conflict; conventionally, major (and minor) mode keybindings start with the prefix C-c
("Ctrl-C") to avoid clashes.
Emacs automatically knows which major mode to use for when visiting a new file thanks to patterns and functions defined in a variable named auto-mode-alist
, which we'll look at a little later.
1.2.4 Minor Modes
In addition to a single major mode, a buffer can have one or more minor modes. Minor modes add additional functionality independently of major modes, such as linum-mode
, which shows line numbers in the left margin, and auto-complete
or company
, which provide auto-completion. Many minor modes work across a variety of major modes; others augment a specific one, such as elisp-slime-nav-mode
, which adds handy navigation commands to emacs-lisp-mode
.
Like major modes, minor modes can define their own keymaps; bindings in these may override those in the current major mode keymap.
1.2.5 Key Sequences
Throughout the Emacs world, you'll see key sequences, or keys for short, which are simple sequences of keystrokes written like this: C-l
. This example means "press control and l at the same time". C
simply means "Control", and dashes are used to group together keys pressed simultaneously. You'd pronounce this as "Control-l".
Sequences of several keystrokes are separated by spaces. Thus, C-x C-f
is simply shorthand for "press Control and x at the same time, then press Control and f at the same time". You'd read this example aloud as "Control-X Control-F". C-c c
means "press Control and c at the same time, then press c by itself".
M
is short for "Meta", which probably isn't a button on your keyboard, unless you have something like this Space Cadet Keyboard:
Many things in Emacs are they way they are because it was originally written on computers that looked very different from modern ones, as you'll see throughout this book. Keyboards like this are similar to what users of Emacs would probably be using "back in the day". But since modern keyboards don't have a Meta button, the Alt key on your keyboard (or Option on a Mac)3 is used in its place. So M-x
, pronounced "meta-X", is typed by holding down Alt/Option and pressing X.
Keystrokes can use multiple modifiers, such as C-M-x
, which means hold Control and Meta (Alt) and press x; you'd read it as "Control-Meta-X". Shift can be specified with S
, so M-S-v
means "hold Shift and Meta and press v", but would more commonly be written as M-V
. Conventially, multiple modifiers are written in alphabetical order, so you'd write C-M-x
istead of M-C-x
, although they both mean the same thing.
Besides Meta, you might have noticed some of the other wacky keys on the Space Cadet Keyboard, such as Hyper and Super. Emacs understands the Windows Key on a PC or Command on a Mac as Super4, which is written with s
. So s-u
means "Super-U", which means "press Windows/Command and U at the same time".
Emacs also understands the modifiers Hyper, written as H
, and Alt, which is different from Meta and written as A
. On a hardware level, the modifier keys on the left-hand side of your keyboard send codes that are distinct from those on the right-hand side; that is, Left Control and Right Control are two distinct keys. It just so happens that they're usually treated as though they are the same, but you do things like tell Emacs to treat Right Control as Hyper if you are so inclined5.
Certain keys are referred to by name, such as left
for the left arrow key. When writing a named key in a key sequence, you need to wrap it in angle brackets, e.g. <M-left>
.
- TODO - Get a better keyboard image from http://xahlee.info/kbd/lisp_keyboards.html
1.2.6 Commands & Getting Help
When you enter a key sequence like C-x C-f
, Emacs runs the corresponding command. A command is nothing more than an Emacs Lisp function that contains the (interactive)
form, which we'll investigate a little later. In this case, C-x C-f
is bound to the function find-file
. You can also run commands by typing M-x
and entering the name of the command and pressing return; so you could type M-x find-file
instead.
If you ever wonder what a key binding does (or what command it's mapped to), you can type C-h k <key sequence>
(e.g., C-h k C-x C-f
), which opens the documentation for the command in question6. Alternatively, C-h f <function name>
(e.g. C-h f find-file
) will let you look up the documentation of a function by its name. Notice how these bindings both start with the prefix C-h
; the same is true for bindings to other help commands7. In this case, the h
in C-h
is short for "help"; you'll have an easy time remembering Emacs keybindings if you remember them this way (C-h k
is help keys", C-h f
is "help function").
You can see a list of all commands bound to a key sequence starting with some prefix by typing <prefix> C-h
. So C-h C-h
will give you a list of all the commands that can be invoked with a key sequence that starts with C-h
.
You'll be learning many such keybindings in your quest for Emacs greatness. This is good, however; learn them well enough and you'll be able to edit code faster than you ever thought possible. Personally, I find it extremely helpful to write key bindings I'm trying to memorize on sticky notes and stick them to the edge of my screen until I've memorized them. Cheatsheets are helpful too; at one point or another, I've taped a few to my desk. You can also try writing them on yourself with a Sharpie, and uploading selfies of your new tatoos to Instagram with the hashtag #EmacsLyfe
.
While we're on the subject, another extremely useful keybinding is C-h v
(describe-variable
), which lets you look up the documentation of a variable by name.
1.2.7 The Init File – ~/.emacs.d/init.el
In 2015, your Emacs customizations live in the init file, located at ~/.emacs.d/init.el
. In the past, they lived in ~/.emacs
, but you'll want to put your init file in a directory in case you decide to break it out into smaller files once it gets big and hairy.
1.2.8 Opening init.el
Let's get started writing some Emacs Lisp! You can open a file using the find-file
command discussed above (in Emacs, opening a file is usually called "visiting" a file). We'll use it to open our init file, ~/.emacs.d/init.el
, so we can tell Emacs not to show us that annoying splash screen. So type C-x C-f
(or M-x find-file
), and you'll see a prompt at the bottom of the screen that says "Find file:". The bottom part of the screen is what's known as the minibuffer. We'll discuss it in more detail later, but for now just know that commands can, and often do, prompt you for input here, and messages are frequently displayed there.
Go ahead and type the following code:
(setq inhibit-splash-screen t)
Save the file by typing C-x C-s
, then close Emacs by typing C-x C-c
. Again, notice how these commands all start with the prefix C-x
. Many important global Emacs commands start with the prefix C-x
, so just remember s
for save and c
for close.
1.2.9 Packages
Before we go any futher, you'll want to get Emacs configured to install packages. Add the following to your init file:
(require 'package) ; TODO - is this neccesary ? (package-initialize) (nconc package-archives '(("melpa" . "http://melpa.milkbox.net/packages/") ("marmalade" . "http://marmalade-repo.org/packages/")))
Now you'll be able to install third-party packages through the Package Menu (M-x package-list-packages
) or via M-x package-install RET [package-name]
.
2 Speak with an Emacs Lisp
2.1 Emacs Lisp Types
If you've picked up a book on Emacs Lisp, you probably already know languages in the Lisp family look a little different than ones from the Fortan/C tradition. So I'm going to try to harp on these differences as little as possible. Just know that the syntax is a little different. But then again, Objective-C's syntax is different too, and that didn't stop people from writing iPhone apps, did it?
If you'd like to follow along with the examples, I'd recommend using ielm
(M-x ielm
), the Emacs Lisp REPL, to interactively evaluate Emacs Lisp expressions. Alternatively, you can visit an Emacs Lisp file, enter and expression, and type C-x C-e
(eval-last-sexp
) to evaluate the symbolic expression that precedes the cursor, and display the results in the minibuffer.
2.1.1 Hello, World! (Everything is an Expression)
Let's start with a traditional example:
(message "Hello, World!")
Evaluating that code will display the message Hello, World!
in the minibuffer, and add it to the end of the *Messages*
buffer. Lisp stands for LISt Processing; all code in Lisp is written in lists, denoted by parentheses. Lists are composed of atoms (symbols, strings, numbers, and the like) and of other lists. The list in the example above is composed of two atoms: the symbol message
, and the string Hello, World!
.
Formally, atoms and lists are collectively known as symbolic-expressions, or s-expressions or sexps for short. Every sexp produces a result when evaluated; atoms like strings or numbers evaluate to themselves, but variables evaluate to their value. Even if...else
constructs return a value; contrast that with more conventional languages, where there is a separate concept of a statement that does not return a value.
When a list is evaluated, the first item is treated as a function, and all other items are evaluated and then passed to that function as arguments. Written in a more conventional language, the example above would look something like:
message("Hello, World!");
This syntax can take some getting used to, but it makes Lisp extremely flexible and powerful, as you'll see later when we discuss macros. It also has some advantages. In other languages, you might write:
1 + 2 + 3
But in Emacs Lisp, the function +
can accept a variable number of arguments:
(+ 1 2 3)
As do functions like >
, which returns t
(true) if each arg is greater than the next:
(> 3 2 1)
Unlike many other languages, +
and >
aren't special built-in operators. They're regular functions, just like any other, and you can replace them if you're so inclined.
As aforementioned, you can nest lists:
(* (+ 1 2) 4)
Since the arguments of a function are evaluated first, this becomes:
(* 3 4)
which evaluates to 12
. You can prevent a list from being evaluated by using the special form quote
:
(quote (1 2 3))
Evaluating the form above will return the list (1 2 3)
. Because quoting lists and symbols is quite commonplace in Lisp code, as a bit of syntactic sugar you can instead simply put a single quote in front of the sexp in question:
'(1 2 3)
Lisp has just a tiny handful of special forms like quote
; the rest of the language can be defined by simply building upon these special forms.
2.1.2 t and nil
nil
is used to represent falsity and nothingness (null
and false
in other languages). t
is used to represent true, but everything that is non-nil is considered true. The empty list is the same as nil
, for reasons we'll discuss later in this chapter.
2.1.3 Comments
Comments in Lisp start with a ;
and extend to the end of the line. In Emacs Lisp convention, comments that share a line with Lisp source code start with a single semicolon; comments that are on their own line start with two semicolons; comments serving as headers denoting large chunks of code start with 3.
;;; Big Chunk Of Code Starts Here '() ; the empty list (nil) ;; TODO - Check logic here (when (> x 100) ;; if x > 100 ... (message "OK!"))
Unlike Common Lisp or Scheme, Emacs Lisp does not syntax for multiple-line comments.
2.1.4 Symbols
Symbols are uniquely-named global objects, used for referring to variables and functions, stored in an internal table. When the reader (i.e., interpreter) encounters a symbol, it looks for an existing object with the corresponding name in an internal table and returns it if found; otherwise a new symbol is created, added to the table, and then returned. This process is known as interning a symbol.
These symbol objects have several "cells", such as its current value as a variable. Evaluating a symbol directly will return this value; you can quote it to prevent this from happening:
major-mode ; -> emacs-lisp-mode 'major-mode ; -> major-mode
Emacs Lisp has no namespaces. Like other such languages, it is customary to prefix the name of every symbol in a package/library with the name of that package and a hypen (package-symbol
). Thus, symbols from dired.el
start with dired-
, such as dired-recursive-copies
. You'll also occasionally see prefixes like package/symbol
or package:symbol
. Note that symbols in Emacs Lisp can contain many characters that would be invalid in other languages: they can be anything except whitespace and these: ( ) " , ' ` ; # | \ _ [ ]
. A variable that is should be treated as private is usually prefixed with pacakge-name--
.
You can get the string name of a symbol using the function symbol-name
:
(symbol-name 'emacs-lisp-mode) ; -> "emacs-lisp-mode"
2.1.5 Variables
You can set a variable using set
:
(set 'require-final-newline t) ; add a final newline to files when saving
(Technically, what you're doing here is setting the symbol require-final-newline
's current value as a variable to t
.) Note how you have to quote require-final-newline
to prevent it from being evaluated. Because this pattern is so common, the variation setq
(SET Quoted) exists:
(setq require-final-newline t) ; add a final newline to files when saving
You can also set multiple variables at once with setq
:
(setq echo-keystrokes 0.1 ; show keystrokes in progress in minibuffer sooner select-enable-clipboard t) ; cutting and pasting uses the system clipboard
2.1.6 Functions
You can define a function with defun
, whose syntax looks like:
(defun function-name args-list body-forms...)
A function consists of a name, a list of arguments, and any number of forms that make up the function's body. When called, the result of function is the result of its last form – no return
statements here.
;; Define a new function (defun square (x) (* x x)) ;; Now call it (square 12) ; -> 144
In JavaScript, this might look like:
function square(x) { return x * x; } square(12); // -> 144
In addition to variable cells, symbols have a function cell. So what you're really doing when you're calling defun is setting the function definition of the symbol square.8
Functions are treated just like any other object in Lisp: they can be passed as arguments, created on the fly, and returned from functions.
2.1.7 Emacs Lisp is a Lisp-2
Since a symbol has both a variable cell and a function cell, it's what's known as a Lisp-2 (as is Common Lisp). The value that you get when you evaluate it depends on the context: evaluate it at the beginning of a list, and its function definition is used; otherwise, its value as a variable is returned. Thus, you can do something like this:
;; Set the variable value of x (setq x 100) ;; Set the function definition of x (defun x (y) (+ y 1)) ;; Evaluating x as an atom gives us its variable value x ; -> 100 ;; When x is at the beginning of a list, it is used as a function (x x) ; -> 101
The term "Lisp-2" simply refers to the fact that Emacs Lisp has 2 separate "namespaces" for functions and variables9, as opposed to a single combined one, such as in a Lisp-1 such as Scheme or Clojure:
;; Define x as the value 100 (def x 100) ;; Define x as a function (overwriting its previous value) (defn x [y] (+ y 1)) x ; -> #<fn ...> (x x) ; Error! Cannot cast function to a number
There are pros and cons to each approach. In Lisp-1 languages, you'll often see weird function parameter names like varr
and klass
to avoid overshadowing functions like var
and class
, respectively. However, in some ways it is "cleaner" – for example, you can call a function passed as a parameter directly:
(defn call-some-fn [f] (f))
In Lisp-2 languages, however, you have to use funcall
to call a function passed as a parameter:
(defun call-some-fun (f) (funcall f))
We'll examine funcall
more a bit later. Emacs Lisp takes advantage of its separate namespaces in many places. For example, minor modes usually define a variable that tells you whether it is currently active, and a function that can be used to turn it on or off:
;; Variable whitespace-mode tells you whether the minor mode is active whitespace-mode ; -> nil ;; toggle it (whitespace-mode) ;; Check the variable again whitespace-mode ; -> t
2.1.8 Buffer-Local Variables
make-local-variable make-variable-buffer-local setq-default setq-local kill-local-variable kill-all-local-variables
2.1.10 Cons Cells
(x . y)
2.1.11 Chars & Strings
2.1.12 Keywords
2.1.13 Association Lists
2.1.14 Property Lists
2.1.15 Vectors + Hash Maps ?
2.1.16 Registers
2.2 Controlling Flow
2.2.1 progn
2.2.2 if
Like many other languages, Emacs Lisp provides an if...else
construct for controlling program execution. Unlike other languages, it is an expression and returns a value. In fact, it is somewhat similar to the ternary if operator found in the C family of languages:
int error_code = explosion_count() > 0 ? 1 : 0;
In Emacs Lisp, an if
statement takes the form
(if condtion then-form else-form)
So the example above would look like the following in Emacs Lisp:
(setq error-code (if (> explosion-count 0) 1 0))
2.2.3 when, unless, cond
2.2.4 Predicate Functions
2.2.5 mapcar
2.2.6 mapc
2.2.7 mapconcat
2.2.8 maphash
2.2.9 dolist
2.3 Functions
2.3.1 #' (function) vs ' (quote)
- Allows byte compiler to check if you have used function names that are actually defined
- Makes Intention Clearer
- Documentation for
function
:Like `quote', but preferred for objects which are functions. In byte compilation, `function' causes its argument to be compiled. `quote' cannot do that.
- As in Common Lisp, lambda is a macro that expands to #'(lambda …)
- Was different on old versions of Emacs. Needed for cl-flet and cl-labels
- Funcall Behavior & cl-flet
2.3.2 Lambdas
2.3.3 &rest args
2.3.4 Interactive
2.3.5 funcall
2.3.6 fboundp
2.3.7 symbol-function
2.3.8 apply
2.3.9 apply-partially
2.3.11 defsubst – inline functions
2.3.12 fset / defalias
2.3.13 indirect-function
2.4 Macros
Ask any Lisper what makes Lisp special and macros are sure to be mentioned. I'm sure you've heard the hype before: Lisp macros are magical, they make it more powerful than any other language, they will clean your house and make you dinner, etc. etc. So I'll save my breath and skip the macro hype. If you haven't drank the macro Kool-Aid just yet I'd highly recommend you read Paul Graham's Beating the Averages (http://www.paulgraham.com/avg.html), which explains why macros are so special better than I could ever hope to do.
Before we jump into discussing macros themselves, you'll need to understand how backquoting works.
2.4.1 Backquoting
The backquote, also known as backtick or syntax quote, makes it easy to construct a list where some elements are evaluated and others aren't. Let's say you want to define a function that takes one argument and will return a list with the elements a
, b
, and that argument.
(defun a-b-arg (x) (list 'a 'b x)) (a-b-arg 'c) ; -> (a b c)
This is a bit of a pain, since you have to quote every form that you don't want to be evaluated. You can't quote the entire list, because then x
would never get evaluated:
(defun a-b-arg (x) '(a b x)) (a-b-arg 'c) ; -> (a b x)
That's not what we wanted! Luckily, backquoting exists to ease our pain:
(defun a-b-arg (x) `(a b ,x)) (a-b-arg 'c) ; -> (a b c)
Using backquote is just using regular quote, but any forms that are preceded with a comma, or unquote, are evaluated and the result is subsituted in its place. You can also use ,@ (unquote-splicing) to splice arguments into a list:
(defun a-b-args (&rest args) `(a b ,@args)) (a-b-args 'c) ; -> (a b c) (a-b-args 'c 'd) ; -> (a b c d)
When ,@ precedes a form, it is evaluated, and the elements of the resulting list are spliced into the list in its place. You can nest multiple levels of backquoting and unquoting as well:
(defun a-b-doubled-args (&rest args) `(a b ,@(mapcar (lambda (x) `(,x ,x)) args))) (a-b-doubled-args 'c 'd) ; -> (a b (c c) (d d))
The mapcar
form is evaluated and generates the list ((c c) (d d))
, which is then spliced into the final result. Backquoting will become your best friend when writing macros, as we'll see shortly.
2.4.2 Macros
So, what is a macro? It's code that writes code. A macro is very much like a function that recieves its arguments before they're evaluated, and returns a new sexp to be evaluated in its place. This process is known as macro expansion. Let's look at a real world example. Here's how unless
could be implemented:
(defmacro my-unless (condition &rest body) `(when (not ,condition) ,@body)) ;; Whenever you type (my-unless (= 1 2) (message "new-math-mode is not enabled.")) ;; It is expanded and replaced with: (when (not (= 1 2)) (message "new-math-mode is not enabled."))
Just like the functions we defined in the previous section, my-unless
uses backquoting to return a new list. The key difference here is that the reader then replaces the my-unless
form with the result before anything is ever evaluated.
Macros can return forms that contain macros, which are then themselves expanded.
(defmacro my-when (condition &rest body) `(my-unless (not ,condition) ,@body)) (my-when (= 1 2) (message "new-math-mode is enabled.")) ;; The first time this is expanded, it becomes: (my-unless (not (= 1 2)) (message "new-math-mode is enabled.")) ;; After being expanded again, it becomes: (when (not (not (= 1 2))) (message "new-math-mode is enabled."))
When writing macros, you'll find it very helpful to see such expansions, one step at a time. You can use the function macroexpand-1
to expand a macro:
(macroexpand-1 '(my-when (= 1 2) do-something)) ;; -> (my-unless (not (= 1 2)) do-something)
Note you must quote the macro form when passing it to macroexpand-1
. Similarly, macroexpand
will expand a macro, but instead of doing it one step at a time, it will completely expand the form, which is rarely as useful. More useful still is pp-macroexpand-expression
, which is similar to macroexpand-1
but pretty-prints the results in a new buffer.
You can use the command pp-macroexpand-last-sexp
, which calls pp-macroexpand-expression
on the sexp before point
. You can call it again on resulting form in the new buffer, allowing you to step through the expansion of a macro. This command is so handy I'd recommend binding it; I like C-c RET
because it's used for the same purpose in other Lisp modes (cider
, for Clojure development, is one example).
Also worth a look is the macrostep
package. macrostep
provides a minor mode, macrostep-mode
, that when enabled lets you interactively expand and collapse macros one step at a time in your Emacs Lisp sources themselves.
Let's look at some ways macros are commonly used in the real world.
2.4.3 "def" Macros
You'll often see macros that make it easier to define something, such as defun
or even defmacro
itself. Conventionally such macros have names that start with def
. Let's say you wanted to create a new version of defvar
that would add every var created with it to a secret list that you could use for nefarious purposes. We'll call it def-tracked-var
.
(defvar tracked-vars '() "Variables created with `def-tracked-var.") (defmacro def-tracked-var (symbol &rest args) `(progn (add-to-list 'tracked-vars ',symbol) (defvar ,symbol ,@args))) (def-tracked-var b 100 "Number of friends to recommend this book to") ;; this expands to: (progn (add-to-list 'tracked-vars 'b) (defvar b 100 "Number of friends to recommend this book to")) ;; Which is then evaluated. b ; -> 100 tracked-vars ; -> '(b)
Note our macro returns a progn
, since macros can only return a single form. Notice too how we use both a quote and and unquote with symbol
. Recall that 'arg is just shorthand for (quote arg)
, so ',arg just becomes (quote ,arg)
; this allows us to pass a 'b to add-to-list
instead of b
, which is what we want in this case.
defvar
takes 3 args, the last of which is optional. But since def-tracked-var
is only concerned with the first we can just gather the rest of the arguments to def-tracked-var
with &rest
and unquote-splice them into the defvar
form we create.
2.4.4 Abstracting Away Boilerplate & Common Patterns
It's obnoxious to write similar-looking boilerplate code over and over (unless you are a fan of Java). Worse yet, it wastes space, distracts readers from the important parts of your code, and can lead to bugs if you write said boilerplate wrong. Wouldn't it be nice if we could just tell computers how to write this code for us? They can! So long as you're writing Lisp.
When byte compiling an Emacs Lisp file, the byte compiler will complain when it sees functions it doesn't know to be defined. You can use declare-function
to tell it that a function exists in a certain file:
(declare-function paredit-backward-delete "paredit")
But what if you want you want to tell it two functions exist in paredit.el
? You'd have to use two calls to declare-function
:
(declare-function paredit-backward-delete "paredit") (declare-function paredit-doublequote "paredit")
Why couldn't we just use mapc
?
(mapc (lambda (f) (declare-function f "paredit")) '(paredit-backward-delete paredit-doublequote))
declare-function
is itself a macro, and since f
is passed to declare-function
before it's evaluated, we're effectively just telling the byte compiler that a function named f
exists in paredit.el
, twice. Which is not at all what we want. Instead, we could use backquoting to create declare-function
forms and call eval
on them:
(mapc (lambda (f) (eval `(declare-function-2 ,f "paredit"))) '(paredit-backward-delete paredit-doublequote))
This would work, in theory, but eval
forms are evaluated at runtime and not by the byte compiler. So we'll still see warnings. And what if we wanted to declare functions in multiple files? Let's write a macro instead:
;; Switch the position of the file argument to the front ;; so we can accept a variable number of functions. (defmacro declare-functions (file &rest functions) (declare (indent 1)) `(progn ,@(mapcar (lambda (f) `(declare-function ,f ,file)) functions))) (declare-functions "paredit" paredit-backward-delete paredit-doublequote paredit-newline) ;; Expands to: (progn (declare-function paredit-backward-delete "paredit") (declare-function paredit-doublequote "paredit") (declare-function paredit-newline "paredit"))
Much better! Notice how we added (declare (indent 1))
to the Emacs how to indent our macro. You could instead define the macro recursively, if you were so inclined:
(defmacro declare-functions (file fn &rest more) (declare (indent 1)) `(progn (declare-function ,fn ,file) ,(when more `(declare-functions ,file ,@more)))) ;; The previous example... (declare-functions "paredit" paredit-backward-delete paredit-doublequote paredit-newline) ;; Expands to: (progn (declare-function paredit-backward-delete "paredit") (declare-functions "paredit" paredit-doublequote paredit-newline)) ;; Which then expands to: (progn (declare-function paredit-backward-delete "paredit") (progn (declare-function paredit-doublequote "paredit") (declare-functions "paredit" paredit-newline))) ;; And finally: (progn (declare-function paredit-backward-delete "paredit") (progn (declare-function paredit-doublequote "paredit") (progn (declare-function paredit-newline "paredit") nil)))
Here, we take the first arg after file and create a declare-function
form. Then, if &rest more
is non-nil (i.e., non-empty), we create a recursive declare-functions
form with the remaining arguments. Each iteration of the macroexpansion generates a single declare-function
form. There's no real benefit to defining declare-functions
recursively, but there are cases where it makes sense to define a macro this way.
2.4.5 Wrapper Macros
(defmacro cam/suppress-messages (&rest body) (declare (indent 0)) `(cl-letf (((symbol-function 'message) (lambda (&rest _)))) ,@body))
2.4.6 with- macros
3 Intermediate
3.1 Defining Code to Be Ran On a Later Occasion
Emacs is flexible and customizable. Perhaps you want to convert all tabs to spaces in files before they're saved to disk. Maybe you want to tell a command to re-indent the current line whenever it's ran. Or maybe you want to run code after a certain package is loaded. We'll be looking at how to do all of these things in this chapter.
3.1.1 Hooks
A hook is simply a list of functions10 that are ran at some specific occasion. Hooks exist for a wide variety of occasions. before-save-hook
is ran whenever a user saves a buffer, before it's actually written to disk; here you might want to add functions to reindent your code or sort your imports. after-save-hook
is ran after the file is written; perhaps this is a good place to restart some external process? Every major mode is supposed to run a mode hook (which is just the name of the major mode with -hook
appended) as the end of its initialization; many minor modes run modes hooks as well. emacs-lisp-mode-hook
gets ran right as emacs-lisp-mode
finishes initializing. Maybe you want to add a hook function to enable a minor mode?
To add a function to a hook, you use the function add-hook
:
(add-hook 'emacs-lisp-mode-hook #'paredit-mode)
Whenever you visit an Emacs Lisp file, emacs-lisp-mode
is activated, and the functions in emacs-lisp-mode-hook
are ran, including #'paredit-mode
. Recall that minor mode functions toggle minor modes in the current buffer when called without an arg; since the minor mode was off to begin with, it will now be enabled.
The best part about hooks is that they're automatically created if needed when you call add-hook
. This means you don't need to call (require 'clojure-mode)
before adding a function to clojure-mode-hook
. You can add custom hooks for hundreds of major modes in your init.el
without sacrificing your Emacs startup speed.
Instead of adding a named function to a hood, you can add a lambda:
(add-hook 'before-save-hook (lambda () ;; Untabify the current buffer (untabify (point-min) (point-max))))
Generally, however, it's probably worth the trouble of upgrading the lambda in question to a named function, since you'll be able to modify the hook function without touching the hook itself, which is handy if you accidently make a mistake in your hook function. Either way, you can remove a hook function by calling remove-hook
; this works for both function symbols and lambdas. Alternaltively, you can simply setq
a hook to nil
and re-add the desired hook functions.
add-hook
accepts two optional arguments: append
and local
. By default, hooks function are added to the beginning of a hook, meaning it will run before any previously added hooks. By passing a non-nil value for append
, the hook function will be added to the end instead. By default, it also adds hook functions to the global value of a hook. This isn't an issue when adding functions to a major mode hook, which only affect certain buffers. But the example above that untabifies the current buffer will run when any buffer is saved. There are cases where this probably isn't what you want – consider a Makefile
, where tabs are neccesary. If you save one after adding this hook, your tabs will be no more.
Passing a non-nil value for local
makes the hook buffer-local in the current buffer and modifies that value instead of the global one. It also adds t
to the buffer-local value, which tells run-hooks
to run the functions in the global value as well as those in the buffer-local one. The example above can be better rewritten as:
(defun my-untabify-buffer () (untabify (point-min) (point-max))) (defun my-emacs-lisp-mode-setup () (add-hook 'before-save-hook #'my-untabify-buffer nil :local)) (add-hook 'emacs-lisp-mode-hook #'my-emacs-lisp-mode-setup)
Now, we're adding a buffer-local hook to untabify the current buffer whenever we enter emacs-lisp-mode
. Whenever you save an Emacs Lisp file, #'my-untabify-buffer
is called; your Makefiles are unaffected.
append
and local
just check for any non-nil value, so you can pass it anything you want. I find it more readable to use a keyword like :append
than simply using t
; it makes it clear what the argument you're passing means.
Most hooks are normal hooks, which means its functions are called with no arguments, and there return values aren't used in any way. By convention, normal hooks are any hook whose name ends in -hook
. Abnormal hooks, on the other hand, are hooks whose functions are passed arguments, whose return values are used, or both. By convention, abnormal-hooks have names ending in -functions
. There are also abnormal hooks whose value is a single function (as opposed to a list of functions); these have names that end in -function
.
You can run hooks with the function run-hooks
, which accepts a variable number of arguments:
(run-hooks 'before-exploding-hook 'before-crashing-and-burning-hook)
run-hooks
simply calls the functions in the hook one-by-one, as with add-hook
, this symbol doesn't actually have to be defined as a variable anywhere.
There are several variations on run-hooks
, such as run-hook-with-args
, run-hook-with-args-until-failure
, run-hook-with-args-until-success
, and run-hook-wrapped
, which can be used to run abnormal hooks.
A major mode is supposed to finish initialization by calling change-major-mode-after-body-hook
, its mode hook, and after-change-major-mode-hook
. The function run-mode-hooks
is provided to run these hooks.11
The following is a partial list of some of the standard hooks:
Hook Name | Description |
---|---|
active-mark-hook | Called when the mark becomes active. |
after-change-functions | Called after each text change. |
after-change-major-mode-hook | Called immediately after a major mode hook. |
after-init-hook | Ran after Emacs initialization has |
completely finished. | |
after-insert-file-functions | Called after insert-file-contents. |
after-make-frame-functions | Ran after a frame is created. |
after-save-hook | Ran after a buffer is saved to its file. |
auto-save-hook | Ran just before auto-saving. |
before-change-functions | Called before each text change. |
before-hack-local-variables-hook | Ran before setting file-local variables, |
after checking for unsafe/risky ones. | |
Only called if there are file-local | |
variables to set. | |
before-init-hook | Ran before loading init files. |
before-make-frame-hook | Ran before a frame is created. |
before-save-hook | Ran before a buffer is saved to its file. |
buffer-access-fontify-functions | List of functions called by buffer-substring |
to fontify if neccessary. | |
buffer-list-update-hook | Ran when the buffer list changes. |
buffer-quit-function | Function to call to quit the current buffer, |
or nil if none. | |
change-major-mode-after-body-hook | Called immediately before a major mode hook. |
change-major-mode-hook | Ran before changing the major mode. |
command-line-functions | List of functions to process unrecognized |
command-line arguments. | |
deactive-mark-hook | Called when the mark becomes inactive. |
delete-frame-functions | Ran before deleting a frame. |
delete-terminal-functions | Ran when a terminal is deleted. |
echo-area-clear-hook | Ran when clearing the echo area. |
emacs-startup-hook | Ran after loading init files and handling |
the command line. | |
find-file-hook | Called after a buffer is loaded from a file. |
find-file-not-found-functions | Functions to be called for find-file on a |
non-existent file. | |
first-change-hook | Called when changing an unmodified buffer. |
focus-in-hook | Ran when a frame gains input focus. |
focus-out-hook | Ran when a frame loses input focus. |
frame-auto-hide-function | Function called to automatically hide |
frames. | |
hack-local-variables-hook | Ran after processing a file's local |
variables | |
kill-buffer-hook | Ran when a buffer is killed. |
kill-buffer-query-functions | Functions to call before killing a buffer. |
If any of them returns nil, the buffer will | |
not be killed. | |
kill-emacs-hook | Ran when kill-emacs is called. |
kill-emacs-query-functions | Functions to call before killing Emacs. If |
any of these functions retuns nil, killing | |
Emacs is cancelled. | |
menu-bar-update-hook | Ran to update menu definitions before |
redisplaying the menu bar. | |
minibuffer-exit-hook | Ran just after exit from the minibuffer. |
minibuffer-setup-hook | Ran just after entry to the minibuffer. |
pop-up-frame-function | Function used by display-buffer for |
creating a new frame. | |
post-command-hook | Ran after each command is executed. |
post-gc-hook | Ran after garbage collection finishes. |
post-self-insert-hook | Ran at the end of self-insert-command, |
after inserting the character. | |
pre-command-hook | Ran before each command is executed. |
split-window-preferred-function | Function called by display-buffer to split |
a window. | |
suspend-hook | Ran before suspending Emacs. |
suspend-resume-hook | Ran after Emacs is unsuspended. |
temp-buffer-setup-hook | Ran at the start of |
with-output-to-temp-buffer; the temporary | |
buffer is current when the hook runs. | |
temp-buffer-show-hook | Ran by with-output-to-temp-buffer after |
displaying the buffer. | |
window-configuration-change-hook | Called when the window configuration |
changes. | |
window-scroll-functions | Functions to call before redisplaying a |
buffer when scrolling. | |
window-setup-hook | Similar to emacs-startup-hook, but runs after |
frame parameters have been set up in response | |
to any settings in the init file. | |
window-size-change-functions | Functions called before redisplay when a |
window's size changes. | |
window-text-change-functions | Functions to call in redisplay when text in |
the window might change. | |
write-contents-functions | Called before writing out a buffer to a file. |
If one of them returns non-nil, the file is | |
considered already written, and the rest of | |
the functions are skipped, as are the ones | |
in write-file-functions. | |
write-file-functions | Called before writing out a buffer to a file. |
If one of them returns non-nil, the rest are | |
skipped. | |
write-region-annotate-functions | Called at the start of write-region. |
write-region-post-annotation-function | Called after write-region completes. |
3.1.2 Advice
3.1.3 eval-after-load
3.2 Moving Around
3.2.1 Point
3.2.2 Mark
3.2.3 Positions
3.2.4 Markers
3.3 Working With Buffers, Windows, and Frames
3.4 Operating on Strings
3.5 Packages
3.5.1 Autoloads
3.5.2 Require & Provide
4 Advanced
4.1 Syntax Tables
4.2 Font Locking
4.3 Custom Variables
4.4 Byte Compilation ?
4.5 Keymaps
6 Putting it All Together
6.1 Debugging with Edebug
6.2 Writing a Minor Mode
6.3 Writing a Major Mode
6.3.1 define-generic-mode
6.4 Packaging
7 Possibilities
7.1 Keyboard Macros
7.2 identity, ignore
7.3 Primitive Functions ?
Footnotes:
It's possible to create a frame without a minibuffer window, but it must use the minibuffer of some other frame. You can do this passing the frame parameter minibuffer
when creating it; pass an existing minibuffer window, or nil
for it to use the minibuffer of default-minibuffer-frame
.
The Emacs DIRectory EDitor.
If you're using Emacs in terminal (-nw
) mode, you might have to tell your terminal to use Alt/Option as Meta (this is the case on OS X). But I'd strongly recommend using Emacs in GUI mode. Terminals are limited. Try typing C-i
and TAB
. They're the same thing!
Unless you're using Emacs in terminal (-nw
) mode, in which case you're SOL.
You can tell Emacs to interpret keycodes for the modifier keys on either side of your keyboard differently by setting corresponding variables. On a Mac, for example, (setq ns-right-command-modifier 'hyper)
tells Emacs to treat the right command key as hyper
instead of super
. Other similar variables are prefixed with ns-
. On Windows, similar variables such as w32-rwindow-modifier
exist. Alternatively, you can also use C-x @ <modifier letter> <key sequence>
to simulate modfier keys like Hyper. C-x @ h g
will be understood as h-g
, but that's a lot of typing.
This is also a good way to see if a key sequence isn't bound to any command, as it will give you an error if it isn't.
You can use the <f1>
prefix instead of C-h
for invoking help commands, too.
Actually, defun
is a just a macro that rewrites the sexp as a defalias
form that sets the function definition of a symbol to an anonymous function. Internally, defalias
usually calls fset
, which actually does the function cell-setting.
Lisps with more than one namespace are also referred to as Lisp-Ns, since Common Lisp actually has at least 7 namespaces, according to Peter Norvig in Paradigms of AI Programming.
A hook can also be a single function, but this usage is obsolete. Calling add-hook
on such a hook will automatically upgrade it to a list.
The rules are a little different for derived major modes, which we'll get into a little bit later.