Elispelem
Elispelem
*
Protesilaos Stavrou
Contents
1 Getting started with Emacs Lisp 2
2 Evaluate Emacs Lisp 3
3 Side eect and return value 5
4 Buers as data structures 6
5 Text has its own properties 8
6 Symbols, balanced expressions, and quoting 9
7 Evaluate some elements inside of a list 13
8 Evaluation inside of a macro or special form 14
9 Mapping through a list of elements 20
10 The match data of the last search 24
*
info@protesilaos.com
1
11 Switching to another buer, window, or narrowed state 27
12 Basic control ow with if, cond, and others 28
13 Control ow with if-let* and friends 32
14 Pattern match with pcase and related 34
15 Run some code or fall back to some other code 36
16 When to use a named function or a lambda function 40
17 Make your interactive function also work from Lisp calls 41
18 COPYING 43
19 GNU Free Documentation License 43
20 Indices 43
20.1 Function index . . . . . . . . . . . . . . . . . . . . . . . . . . 43
20.2 Variable index . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
20.3 Concept index . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2
It is a recreational activity that expands your horizons. Plus, you cultivate
your Elisp skills, which can prove helpful in the future, should you choose to
modify some behaviour of Emacs.
Tinkering with Emacs is part of the experience. It teaches you to be
unapologetically opinionated about how your editor works. The key is to
know enough Elisp so that you do not spend too much time having fun or
getting frustrated because something trivial does not work. I am writing this
as a tinkerer myself with no background in computer science or neighbouring
studies: I learnt Emacs Lisp through trial and error by playing around with
the editor. My nominal goal was to improve certain micro-motions I was
repeating over and over: I sought eciency only to discover something much
more profound. Learning to extend my editor has been a fullling experience
and I am more productive as a result. Emacs does what I want it to do and
I am happy with it.
Each chapter herein is generally short and to-the-point. Some are more
friendly to beginners while others dive deeper into advanced topics. There
are links between the chapters, exactly how a reference manual is supposed
to be done. You may then go back and forth to nd what you need.
The text you will nd here is a combination of prose and code. The latter
may be actual Elisp or pseudo-code which captures the underlying pattern.
I encourage you to read this book either inside of Emacs or with Emacs
readily available. This way, you can play around with the functions I give
you, to further appreciate their nuances.
The big picture view approach I am adopting is about covering the
concepts that I encounter frequently while working with Emacs Lisp. This
book is no substitute for the Emacs Lisp Reference Manual and should by no
means be treated as the source of truth for any of the Elisp forms I comment
on.
Good luck and enjoy!
3
do not let the implementation detail of interactivity distract you from the
fact that every single action you perform in Emacs involves the evaluation
of Emacs Lisp.
Another common pattern of interaction is with the M-x (execute-extended-command)
key, which by default runs the command execute-extended-command: it
produces a minibuer prompt that asks you to select a command by its
name and proceeds to execute it.
Emacs can evaluate Elisp code from anywhere. If you have some Elisp
in your buer, you can place the cursor at the end of its closing parenthesis
and type C-x C-e (eval-last-sexp). Similarly, you can use the commands
eval-buffer and eval-region to operate on the current buer or high-
lighted region, respectively.
The eval-last-sexp also works on symbols (Symbols, balanced expres-
sions, and quoting). For example, if you place the cursor at the end of the
variable buffer-file-name and use C-x C-e (eval-last-sexp), you will get
nil or the le system path to the
the value of that variable, which is either
le you are editing.
Sometimes the above are not appropriate for what you are trying to do.
Suppose you intend to write a command that copies the le path of the
current buer. To do that, you need your code to test the value of the
variable buffer-file-name (Buers as data structures). But you do not
want to type out buffer-file-name in your actual le, then use one of the
aforementioned commands for Elisp evaluation, and then undo your edits.
That is cumbersome and prone to mistakes! The best way to run Elisp in the
current buer is to type M-: (eval-expression): it opens the minibuer
and expects you to write the code you want to evaluate. Type RET from
there to proceed. The evaluation is done with the last buer as current (the
buer that was current prior to calling eval-expression).
Here is some Emacs Lisp you may want to try in (i) a buer that cor-
responds to a le versus (ii) a buer that is not associated with any le on
disk.
When you are experimenting with code, you want to test how it behaves.
Use the command ielm to open an interactive shell. It puts you at a prompt
4
where you can type any Elisp and hit RET to evaluate it. The return value
*scratch* buer. If it is
is printed right below. Alternatively, switch to the
using the major mode lisp-interaction-mode, which is the default value
of the variable initial-major-mode, then you can move around freely in
that buer and type C-j (eval-print-last-sexp) at the end of some code
to evaluate it. This works almost the same way as eval-last-sexp, with
the added eect of putting the return value right below the expression you
just evaluated.
In addition to these, you can rely on the self-documenting nature of
Emacs to gure out what the current state is. For example, to learn about the
buer-local value of the variable major-mode, you can do C-h v (describe-variable),
and then search for that variable. The resulting Help buer will inform you
major-mode. This help command and many others
about the current value of
like describe-function, describe-keymap, describe-key, and describe-symbol,
provide insight into what Emacs knows about a given object. The Help buer
will show relevant information, such as the path to the le that denes the
given function or whether a variable is declared as buer-local.
Emacs is self-documenting because it reports on its state. You do not
need to explicitly update the Help buers. This happens automatically by
virtue of evaluating the relevant code: Emacs eectively shows you the latest
value of whatever it is you are working with.
5
the side eects. If you are sloppy, you will get unintended results caused
by all those ill-considered changes to the environment. But if you use side
eects meticulously, you are empowered to take Elisp to its full potential.
For instance, imagine you dene a function that follows the logic of create
a buer, go there, write some text, save the buer to a le at my preferred
location, and then come back where I was before I called this function, while
leaving the created buer open. All these are side eects and they are all
useful. Your function may have some meaningful return value as well that
you can employ as the input of another function. For example, your function
would return the buer object it generated, so that the next function can
do something there like display that buer in a separate frame and make its
text larger.
The idea is to manipulate the state of the editor, to make Emacs do
what you envision. Sometimes this means your code has side eects. At
other times, side eects are useless or even run counter to your intended
results. You will keep rening your intuition about what needs to be done
as you gain more experience and expand the array of your skills (Symbols,
balanced expressions, and quoting). No problem; no stress!
6
that (i) opens a le, (ii) goes to a certain position, (iii) copies the text
it found, (iv) switches to another buer, and (v) writes what it found
to this new buer.
Present the results of some operation You may have a function that
shows upcoming holidays. Your code does the computations behind
the scenes and ultimately writes some text to a buer. The end
product is on display. Depending on how you go about it, you will
want to evaluate the function get-buffer-create or its more strict
get-buffer alternative. If you need to clear the contents of an existing
buer, you might use the with-current-buffer macro to temporarily
switch to the buer you are targetting and then either call the function
erase-buffer to delete everything or limit the deletion to the range
betweeen two buer positions with delete-region. Finally, the func-
tions display-buffer or pop-to-buffer will place the buer in an
Emacs window.
The latter point is perhaps the most open-ended one. Buers are like
a bundle of variables, which includes their contents, the major mode they
are running, and all the buer-local values they have. In the following code
block, I am using the seq-filter function to iterate through the return value
of the function buffer-list (Symbols, balanced expressions, and quoting).
(seq-filter
(lambda (buffer)
"Return BUFFER if it is visible and its major mode derives from `text-mode'."
(with-current-buffer buffer
;; The convention for buffers which are not meant to be seen by
;; the user is to start their name with an empty space. We are
7
;; not interested in those right now.
(and (not (string-prefix-p " " (buffer-name buffer)))
(derived-mode-p 'text-mode))))
(buffer-list))
This will return a list of buer objects that pass the test of (i) being
visible to the user and (ii) their major mode is either text-mode or derived
therefrom. The above may also be written thus (When to use a named
function or a lambda function):
As with buers, Emacs windows and frames have their own parameters.
I will not cover those as their utility is more specialised and the concepts are
the same. Just know that they are data structures that you may use to your
advantage, including by iterating through them (Mapping through a list of
elements).
8
describe-char. It will tell you about the character it sees, what font it is
rendered with, which code point it is, and what its text properties are.
Suppose you are writings your own major mode. At the early stage
of experimentation, you want to manually add text properties to all in-
stances of the phrase I have properties in a buer whose major mode is
fundamental-mode, so you do something like this (The match data of the
last search):
(defun my-add-properties ()
"Add properties to the text \"I have properties\" across the current buffer."
(goto-char (point-min))
(while (re-search-forward "I have properties" nil t)
(add-text-properties (match-beginning 0) (match-end 0) '(face error))))
This is some sample text. Will the phrase "I have properties" use the `bold' face?
9
(defun my-greet-person (name)
"Say hello to the person with NAME."
(message "Hello %s" name))
I just dened the function with the name my-greet-person. It has a list
of parameters, specically, a list of one parameter, called name. Then is the
optional documentation string, which is for users to make sense of the code
and/or understand the intent of the function. my-greet-person takes name
and passes it to the function message as an argument to ultimately print
a greeting. The message *Messages* buer,
function logs the text in the
which you can visit directly with C-h e (view-echo-area-messages). At
any rate, this is how you call my-greet-person with the one argument it
expects:
(my-greet-person "Protesilaos")
Even for the most basic tasks, you have lots of parentheses. But fear not!
These actually make it simpler to have a structural understanding of your
code. If it does not feel this way right now, it is because you are not used to
it yet. Once you do, there is no going back.
The basic idea of any dialect of Lisp, Emacs Lisp being one of them, is
that you have parentheses which delimit lists. A list consists of elements.
Lists are either evaluated to produce the results of some computation or
returned as they are for use in some other evaluation (Side eect and return
value):
The list as a function call When a list is evaluated, the rst element is
the name of the function and the remaining elements are the arguments
passed to it. You already saw this play out above with how I called
my-greet-person with "Protesilaos" as its argument. Same prin-
ciple for my-greet-person-from-country, with "Protesilaos" and
"Cyprus" as its arguments.
10
The list as data When a list is not evaluated, then none of its elements
has any special meaning at the outset. They are all returned as a
list without further changes. When you do not want your list to be
evaluated, you prex it with a single quote character. For example,
'("Protesilaos" "Prot" "Cyprus") is a list of three elements that
should be returned as-is.
Consider the latter case, which you have not seen yet. You have a list
of elements and you want to get some data out of it. At the most basic
level, the functions car and cdr return the rst element and the list of all
remaining elements, respectively:
The single quote here is critical, because it instructs Emacs to not eval-
uate the list. Otherwise, the evaluation of this list would treat the rst
element, namely "Protesilaos", as the name of a function and the remain-
der of the list as the arguments to that function. As you do not have the
denition of such a function, you get an error.
Certain data types in Emacs Lisp are self-evaluating. This means that
if you evaluate them, their return value is what you are already seeing.
For example, the return value of the string of characters "Protesilaos" is
"Protesilaos". This is true for strings, numbers, keywords, symbols, and
the specialnil or t. Here is a list with a sample of each of these, which you
construct by calling the function list:
The list function evaluates the arguments passed to it, unless they are
quoted. The reason you get the return value without any apparent changes
is because of self-evaluation. Notice that my-greet-person-from-country
is quoted the same way we quote a list we do not want to evaluate. Without
it, my-greet-person-from-country would be evaluated, which would return
an error unless that was also dened as a variable.
Think of the single quote as an unambiguous instruction: do not eval-
uate the following. More specically, it is an instruction to not perform
11
evaluation if it would have normally happened in that context (Partial eval-
uation inside of a list). In other words, you do not want to quote something
inside of a quoted list, because that is the same as quoting it twice:
12
7 Evaluate some elements inside of a list
You already have an idea of how Emacs Lisp code looks like (Symbols, bal-
anced expressions, and quoting). You have a list that is either evaluated or
taken as-is. There is another case where a list should be partially evaluated
or, more specically, where it should be treated as data instead of a function
call with some elements inside of it still subject to evaluation.
In the following code block, I am dening a variable called my-greeting-in-greek,
which is a common phrase in Greek that literally means health to you and
is pronounced as yah sou. Why Greek? Well, you got the lambda that
engendered this whole business with Lisp, so you might as well get all the
rest (When to use a named function or a lambda function)!
13
(message "%S" `(one two ,(concat my-greeting-in-greek " " "") four))
;; => "(one two \" \" four)"
Bear in mind that you would get an error if you were not quoting this
list at all, because the rst element one would be treated as the symbol a
function, which would be called with all other elements as its arguments.
Chances are that one is not dened as a function in your current Emacs
session or those arguments are not meaningful to it, anyway. Plus, two and
four would then be treated as variables, since they are not quoted, in which
case those would have to be dened as well, else more errors would ensue.
Other than the comma operator, there is the ,@ (how is this even pro-
nounced? comma at, perhaps?), which is notation for splicing. This is
jargon in lieu of saying the return value is a list and I want you to remove
the outermost parentheses of it. In eect, the code that would normally
return '(one two three) now returns one two three. This dierence may
not make much sense in a vacuum, though it does once you consider those
elements as expressions that should work in their own right, rather than
simply be elements of a quoted list. I will not elaborate on an example here,
as I think this is best covered in the context of dening macros (Evaluation
inside of a macro or special form).
Chances are you will not need to use the knowledge of partial evaluation.
It is more common in macros, though can be applied anywhere. Be aware of
it regardless, as there are scenaria where you will, at the very least, want to
understand what some code you depend on is doing.
Lastly, since I introduced you to some Greek words, I am now considering
you my friend. Here is a joke from when I was a kid. I was trying to explain
some event to my English instructor. As I lacked the vocabulary to express
myself, I started using Greek words. My instructor had a strict policy of
only responding to English, so she said It is all Greek to me. Not knowing
that her answer is an idiom for I do not understand you, I blithely replied,
Yes, Greek madame; me no speak England very best. I was not actually a
beginner at the time, though I would not pass on the opportunity to make
fun of the situation. Just how you should remember to enjoy the time spent
tinkering with Emacs. But enough of that! Back to reading this book.
14
evaluation inside of a list). Sometimes though, you look at a piece of code
and cannot understand why the normal rules of quoting and evaluation do
not apply. Before you see this in action, inspect a typical function call that
also involves the evaluation of a variable:
You encountered this code in the section about partial evaluation. What
you have here is a call to the function concat, followed by three arguments.
One of these arguments is a variable, the my-greeting-in-greek. When this
list is evaluated, what Emacs actually does is to rst evaluate the arguments,
including my-greeting-in-greek, in order to get their respective values and
only then to call concat with those values. You can think of the entire
operation as follows:
Here is a list.
It is not quoted.
You now have the value of each of the arguments, including the value
of the symbol my-greeting-in-greek.
Call concat with all the values you got.
In other words, the following two yield the same results (assuming a
constant my-greeting-in-greek):
15
This is predictable. It follows the basic logic of the single quote: if it
is quoted, do not evaluate it and return it as-is, otherwise evaluate it and
return its value. But you will nd plenty of cases where this expected pattern
is seemingly not followed. Consider this common case of using setq to bind
a symbol to the given value:
The above expression looks like a function call, meaning that (i) the list
is not quoted, (ii) the rst element is the name of a function, and (iii) the
remaining elements are arguments passed to that function. In a way, this is
all true. Though you would then expect the my-test-symbol to be treated
as a variable, which would be evaluated in place to return its result which
would, in turn, be the actual argument passed to the function. However, this
is not how setq works. The reason is that it is a special case that internally
does this:
If the normal rules of evaluation applied, then the list of parametes should
be quoted. Otherwise, you would expect (name country) to be interpreted
as a function call with name as the symbol of the function and country as its
argument which would also be a variable. But this is not what is happening
because defun will internally treat that list of parameters as if it was quoted.
Another common scenario is with let (Control ow with if-let* and
friends). Its general form is as follows:
;; This is pseudo-code
(let LIST-OF-LISTS-AS-VARIABLE-BINDINGS
BODY-OF-THE-FUNCTION)
16
The LIST-OF-LISTS-AS-VARIABLE-BINDINGS is a list in which each ele-
ment is a list of the form (SYMBOL VALUE). Here is some actual code:
Continuing with the theme of special forms, if let was a typical function
call, the LIST-OF-LISTS-AS-VARIABLE-BINDINGS would have to be quoted.
Otherwise, it would be evaluated, in which case the rst element would be
the name of the function. But that would return an error, as the name of
the function would correspond to another list, the (name "Protesilaos"),
rather than a symbol. Things work ne with let because it internally does
the quoting of its LIST-OF-LISTS-AS-VARIABLE-BINDINGS.
Expect similar behaviour with many special forms as well as with macros
such as the popular use-package, which is used to congure packages inside
of your Emacs initialisation le. How each of those macros works depends
on the way it is designed. I will not delve into the technicalities here, as I
want the book to be useful long-term, focusing on the principles rather than
the implementation details that might change over time.
To learn what a given macro actually expands to, place the cursor at the
end of its closing parenthesis and call the command pp-macroexpand-last-sexp.
It will produce a new buer showing the expanded Emacs Lisp code. This
is what is actually evaluated in the macro's stead.
With those granted, it is time to write a macro. This is like a template,
which empowers you to not repeat yourself. Syntactically, a macro will most
probably depend on the use of the quasi-quote, the comma operator, and the
mechanics of splicing (Partial evaluation inside of a list). Here is a simple
scenario where we want to run some code in a temporary buer while setting
the default-directory to the user's home directory.
17
collected into a single list called EXPRESSIONS. The judicious use of partial
evaluation ensures that the macro will not be evaluated right now but only
when it is called. The arguments passed to it will be placed where you have
specied. Here is a call that uses this macro:
(progn
(message "Now we are doing something unrelated to the macro")
(my-work-in-temp-buffer-from-home
(message "We do stuff inside the macro")
(+ 1 1)
(list "Protesilaos" "Cyprus")))
my-work-in-temp-buffer-from-home,
If you place the cursor at the closing parenthesis of
you will be able to conrm what it expands to by typing M-x (execute-extended-command)
and then invoking the command pp-macroexpand-last-sexp. This is what
I get:
Piecing it together with the rest of the code in its context, I arrive at
this:
(progn
(message "Now we are doing something unrelated to the macro")
(let ((default-directory "/home/prot/"))
(with-temp-buffer
(message "Running all expression from the `%s' directory" default-directory)
(message "We do stuff inside the macro")
(+ 1 1)
(list "Protesilaos" "Cyprus"))))
18
you this other approach, instead, where I write a macro that lets me dene
several almost identical interactive functions (Make your interactive function
also work from Lisp calls).
The my-define-command can be broadly divided into two parts: (i) what
gets evaluated outright and (ii) what gets expanded for further evaluation.
The latter part starts with the quasi-quote. This distinction is important
when we call the macro, because the former part will be executed right away
so if we hit the error, it will never expand and then run the EXPRESSIONS.
Try pp-macroexpand-last-sexp with the following to see what I mean. For
your convenience, I include the macro expansions right below each case.
(my-define-command first-demo
(message "This is what my function does")
(+ 1 10)
(message "And this"))
;; =>
;;
;; (defun modified-version-of-first-demo nil
;; (interactive)
;; "The difference between `modified-version-of-first-demo' and `first-demo'"
;; (message "This is what my function does")
;; (+ 1 10)
;; (message "And this"))
(my-define-command second-demo
(list "Protesilaos" "Cyprus")
(+ 1 1)
19
(message "Arbitrary expressions here"))
;; =>
;;
;; (defun modified-version-of-second-demo nil
;; (interactive)
;; "The difference between `modified-version-of-second-demo' and `second-demo'"
;; (list "Protesilaos" "Cyprus")
;; (+ 1 1)
;; (message "Arbitrary expressions here"))
Do you need macros? Not always, though there will be cases where a
well-dened macro makes your code more elegant. What matters is that you
have a sense of how evaluation works so that you do not get confused by
all those parentheses. Otherwise you might expect something dierent to
happen than what you actually get.
(mapcar
(lambda (number)
20
(+ 10 number))
'(1 2 3 4 5))
;; => (11 12 13 14 15)
(mapc
(lambda (buffer)
(when (and (buffer-file-name buffer)
(buffer-modified-p buffer))
(save-buffer)))
(buffer-list))
An alternative to the above is dolist, which is used for side eects but
always returns nil:
You will notice that the dolist is a macro, so some parts of it seem to
behave dierently than with basic lists and the evaluation rules that apply
21
to them (Evaluation inside of a macro or special form). This is a matter of
getting used to how the code is expressed.
When to use a dolist as opposed to a mapc is a matter of style. If you
are using a named function, a mapc looks cleaner to my eyes. Otherwise a
dolist is easier to read. Here is my approach with some pseudo-code:
;; I like this:
(mapc #'NAMED-FUNCTION LIST)
While dolist and mapc are for side eects, you can still employ them
in the service of accumulating results, with the help of let and related
forms (Control ow with if-let* and friends). Depending on the specics,
this approach may make more sense than relying on a mapcar. Here is an
annotated sketch:
;; As above but reverse the return value, which makes more sense:
(let ((found-strings nil))
(dolist (element '("Protesilaos" 1 2 3 "Cyprus"))
22
(when (stringp element)
(push element found-strings)))
(nreverse found-strings))
;; => ("Protesilaos" "Cyprus")
(mapcar
(lambda (element)
(when (stringp element)
element))
'("Protesilaos" 1 2 3 "Cyprus"))
;; => ("Protesilaos" nil nil nil "Cyprus")
(delq nil
(mapcar
(lambda (element)
(when (stringp element)
element))
'("Protesilaos" 1 2 3 "Cyprus")))
;; => ("Protesilaos" "Cyprus")
The seq-filter is the best tool when all you need is to test if the element
satises a predicate function and then return that element. But you cannot
return something else. Whereas mapcar will take any return value without
complaints, such as the following:
(delq nil
(mapcar
(lambda (element)
23
(when (stringp element)
;; `mapcar' accumulates any return value, so we can change
;; the element to generate the results we need.
(upcase element)))
'("Protesilaos" 1 2 3 "Cyprus")))
;; => ("PROTESILAOS" "CYPRUS")
(seq-filter
(lambda (element)
(when (stringp element)
;; `seq-filter' only returns elements that have a non-nil return
;; value here, but it returns the elements, not what we return
;; here. In other words, this `lambda' does unnecessary work.
(upcase element)))
'("Protesilaos" 1 2 3 "Cyprus"))
;; => ("Protesilaos" "Cyprus")
How you go about mapping over a list of elements will depend on what
you are trying to do. There is no one single function that does everything
for you. Understand the nuances and you are good to go. Oh, and do
seq library (use M-x (execute-extended-command),
look into the built-in
invoke find-library, and then search for seq). You are now looking at
the source code of seq.el: it denes plenty of functions like seq-take,
seq-find, seq-union. Another way is to invoke the command shortdoc
and read about the documentation groups list as well as sequence.
24
"Search forward for REGEXP and return its match data, else nil."
(when (re-search-forward regexp nil t)
(list
:beginning (match-beginning 0)
:end (match-end 0)
:string (match-string-no-properties 0))))
You may then call it with a string argument, representing an Emacs Lisp
regular expression:
(my-get-match-data "Protesilaos.*Cyprus")
If the regular expression matches, then you get the match data. Here is
some sample text:
Place the cursor before that text and use M-: (eval-expression) to
evaluate my-get-match-data with the regexp I show above. You will get a
return value, as intended.
The way my-get-match-data is written, it does two things: (i) it has
the side eect of moving the cursor to the end of the text it found and (ii)
it returns a list with the match data I specied. There are many scenaria
where you do not want the aforementioned side eect: the cursor should
stay where it is. As such, you can wrap your code in a save-excursion
(Switching to another buer, window, or narrowed state): it will do what
it must and nally restore the point (Run some code or fall back to some
other code):
25
matching text. In practice, this is a useful tool that may be combined with
save-match-data. Imagine you want to do a search forward inside of an-
other search you are performing, such as to merely test if there is a match
for a regular expression in the context, but need to inhibit the modication
of the match data you planned to operate on. As such:
(my-get-match-data-with-extra-check "Protesilaos.*Cyprus")
;; => nil
(my-get-match-data-with-extra-check "Protesilaos.*Cyprus")
;; => (:beginning 41988 :end 42032 :string "Protesilaos lives in the mountains of Cypru
26
11 Switching to another buer, window, or nar-
rowed state
As you use Emacs Lisp to do things programmatically, you encounter cases
where you need to move away from where you are. You may have to switch
to another buer, change to the window of a given buer, or even modify
what is visible in the buer you are editing. At all times, this involves one or
more side eects which, most probably, should be undone when your function
nishes its job (Side eect and return value).
Perhaps the most common case is to restore the point. You have some
code that moves back or forth in the buer to perform a match for a given
piece of text. But then, you need to leave the cursor where it originally
was, otherwise the user will lose their orientation. Wrap your code in a
save-excursion and you are good to go, as I show elsewhere (The match
data of the last search):
(save-window-excursion
(select-window SOME-WINDOW)
MOVE-AROUND-IN-THIS-BUFFER)
27
use save-excursion as the outermost call. Other than that, you will also
nd cases that require a dierent approach to perform some conditional be-
haviour (Run some code or fall back to some other code).
(defun my-15-lines-down ()
"Move at most 15 lines down."
(interactive)
(forward-line 15))
(defun my-15-lines-down-or-up ()
"Move at most 15 lines down or go back if `eobp' is non-nil."
(interactive)
28
(if (eobp)
(forward-line -15)
(forward-line 15)))
(defun my-15-lines-down-or-error ()
"Throw an error if `eobp' returns non-nil, else move 15 lines down."
(interactive)
(if (eobp)
(error "Already at the end; will not move further")
(forward-line 15)))
A quirk of Emacs Lisp, which may be a feature all along, is how indenta-
tion is done. Just mark the code you have written and type TAB: Emacs will
take care to indent it the way it should be done. In the case of the if state-
ment, the then part is further in than the else part of the logic. There
is no special meaning to this indentation: you could write everything on a
single line like (if COND THIS ELSE), which looks like your typical list, by
the way (Symbols, balanced expressions, and quoting). What the indenta-
tion does is help you identify imbalances in your parentheses. If the dierent
expressions all line up in a way that looks odd, then you are most probably
missing a parentheses or have too many of them. Generally, expressions at
the same level will all line up the same way. Those deeper in will have more
indentation, and so on. Experience will allow you to spot mistakes with
mismatching parentheses. But even if you do not identify them, you will get
an error eventually. Rest assured!
The way if is written is like a function that takes two or more arguments.
The or more all counts as part of the else logic. As such, (if COND THIS)
has no else consequence, while (if COND THIS ELSE1 ELSE2 ELSE3) will
run ELSE1, ELSE2, and ELSE3 in order as part of the else branch. Here is
how this looks once you factor in proper indentation:
(if COND
THIS
ELSE1
ELSE2
ELSE3)
29
Now what if the THIS part needs to be more than one function call? Elisp
has the progn form, which you can use to wrap function calls and pass them
as a single argument. Putting it all together, your code will now look this
like:
(if COND
(progn
THIS1
THIS2
THIS3)
ELSE1
ELSE2
ELSE3)
If you do not need the else part, use when to express your inten-
tion. Internally, this is a macro which actually stands for (if COND (progn
EXPRESSIONS)), where EXPRESSIONS is one or more expressions. A when
looks like this:
(when COND
THIS1
THIS2
THIS3)
Similarly, the unless has the meaning of (when (not COND) EXPRESSIONS).
It, too, is a macro that expands to an if statement:
(unless COND
THIS1
THIS2
THIS3)
When the condition you are testing for has multiple parts, you can rely
on and as well as or:
(when (or THIS THAT)
EXPRESSIONS)
30
Depending on the specics of the case, the combination of multiple if,
when, or, and will look awkward. You can break down the logic to distinct
conditions, which are tested in order from top to bottom, using cond. The
way cond is written is as a list of lists, which do not need quoting (Evaluation
inside of a macro or special form). In abstract, it looks like this:
(cond
(CONDITION1
CONSEQUENCES1)
(CONDITION2
CONSEQUENCES2)
(CONDITION3
CONSEQUENCES3)
(t
CONSEQUENCES-FALLBACK))
Each of the consequences can be any number of expressions, like you saw
above with when. This is a toy function to show how cond behaves:
31
(my-toy-cond "")
;; => "You just gave me a blank string; try harder!"
All of the above are common in Emacs Lisp. Another powerful macro is
pcase, which we will consider separately due to its particularities (Pattern
match with pcase and related).
(let BINDINGS
BODY)
The BINDINGS is a list of lists, which does not need to be quoted (Eval-
uation inside of a macro or special form). While BODY consists of one or
more expressions, which I have also named EXPRESSIONS elsewhere in this
book. The dierence between let and let* (pronounced let star) is that
the latter makes earlier bindings available to later bindings. Like this:
32
Sometimes what you want to do is create those bindings ifand only
ifthey are all non-nil. If their value is nil, then they are useless to you,
in which case you do something else (Basic control ow with if, cond, and
others). Values may or may not be nil when you are creating a binding
with the return value of a function call or some other variable. You could
always write code like this:
33
There is no inherently superior way of doing things. It is a matter of
using the right tool for the task at hand. Sometimes you want the bindings
to be created, even if their value is nil. Choose what makes sense.
(pcase major-mode
('org-mode (message "You are in Org"))
('emacs-lisp-mode (message "You are in Emacs Lisp"))
(_ (message "You are somewhere else")))
(cond
((eq major-mode 'org-mode)
(message "You are in Org"))
((eq major-mode 'emacs-lisp-mode)
(message "You are in Emacs Lisp"))
(t
(message "You are somewhere else")))
34
(message
(pcase major-mode
('org-mode "You are in Org")
('emacs-lisp-mode "You are in Emacs Lisp")
(_ "You are somewhere else")))
Go ahead and evaluate that function and then try it out (Evaluate Emacs
Lisp). Below are a couple of examples:
35
is arcane, until you relate it to the quasi-quote and the comma which are
used for partial evaluation (Partial evaluation inside of a list). With this in
mind, consider pcase-let, pcase-let*, pcase-lambda, and pcase-dolist,
as variations of the plain let, let*, lambda, and dolist with the added
feature of supporting destructuring. They are not doing any of the extras of
pcase thoughjust destructuring on top of their familiar behaviour! This
is especially useful when you are working with the return value of a function
which comes as a list. I will not elaborate at length, as this is an advanced
use-case. If you are already at that level, you do not need me to tell you
what to write. For the rest of us who, like me, typically work with simpler
code, the pcase-let serves as a sucient illustration of the principle:
36
needs to do. While the prompt is open, the function highlights all instances
of the regular expression (defun in the current buer. Those highlights must
go away after you are done with the minibuer and its consequences.
(defun my-prompt-with-temporary-highlight ()
"Ask for confirmation and highlight all instances of a regexp while waiting."
(let ((regexp "(defun"))
(unwind-protect
(progn
(highlight-regexp regexp)
(if (y-or-n-p "Should we proceed or not? ")
(message "You have decided to proceed")
(message "You prefer not to continue")))
(unhighlight-regexp regexp))))
Try the above in your Emacs to get a feel for it. While the yes or no
prompt is active, also do C-g (keyboard-quit) or C-] (abort-recursive-edit)
to conrm that the highlights are removed even though the code never gets
past the prompting phase. You may even modify the function to produce an
error: it will create a backtrace, which will still have the eect of unwinding
after you do q (debugger-quit) from the *Backtrace* window.
(defun my-prompt-with-temporary-highlight-try-with-error ()
"Ask for confirmation and highlight all instances of a regexp while waiting."
(let ((regexp "(defun"))
(unwind-protect
(progn
(highlight-regexp regexp)
(error "This error makes no sense here; close the backtrace to test the unwin
(if (y-or-n-p "Should we proceed or not? ")
(message "You have decided to proceed")
(message "You prefer not to continue")))
(unhighlight-regexp regexp))))
Taking a step back, you will gure out how unwind-protect is a more
general form of specialists like save-excursion and save-restriction (Switch-
ing to another buer, window, or narrowed state), while it underpins the
save-match-data (The match data of the last search) among many other
functions/macros, such as with-temp-buffer and save-window-excursion.
What unwind-protect does not do is respond specially to signals, such as
37
those coming from the error function: it will allow the error to happen,
meaning that a backtrace will be displayed and your code will exit right
there (but the unwinding will still work, as I already explained, once you
dismiss the backtrace). To make your code treat signals in a more controlled
fashion, you must rely on condition-case.
With condition-case you assume full control over the behaviour of your
code, including how it should deal with errors. Put dierently, your Elisp
will express the intent of I want to do this, but if I get an error I want
to do that instead. There are many signals to consider, all of which come
from the signal function. These include the symbols error, user-error,
args-out-of-range, wrong-type-argument, wrong-length-argument, and
quit, in addition to anything else the programmer may consider necessary.
In the following code blocks, I show you how condition-case looks like.
Remember that sometimes you do not do quoting the usual way because of
how the underlying form is implemented (Evaluation inside of a macro or
special form). The example I am using is the same I had for unwind-protect.
(defun my-prompt-with-temporary-highlight-and-signal-checks ()
"Ask for confirmation and highlight all instances of a regexp while waiting."
(let ((regexp "(defun"))
(condition-case nil
(progn
(highlight-regexp regexp)
(if (y-or-n-p "Should we proceed or not? ")
(user-error "You have decided to proceed; but we need to return a `user-e
(error "You prefer not to continue; but we need to return an `error'")))
(:success
(unhighlight-regexp regexp)
(message "No errors, but still need to unwind what we did, plus whatever else we
(quit
(unhighlight-regexp regexp)
(message "This is our response to the user aborting the prompt"))
(user-error
(unhighlight-regexp regexp)
(message "This is our response to the `user-error' signal"))
(error
(unhighlight-regexp regexp)
(message "This is our response to the `error' signal")))))
The above function illustrates both the aforementioned concept of un-
winding and the mechanics of handling signals. The abstract structure of
38
condition-case looks to me like an amalgamation of let, unwind-protect,
and cond. These conditions may include the special handler of :success,
as I show there. Granted, the code I wrote will never lead to that specic
success case, though you can modify what happens after the prompt to, say,
call message instead of the user-error function, which will then count as
a successful conclusion. Otherwise, I think the expressions I wrote tell you
exactly how this program responds to the signals it receives.
What I have not covered yet, is the aspect of condition-case that is
like the let, namely, how it binds the error data to a variable within this
scope. In my implementation above, it is the nil you see there, meaning
that I choose not to perform such a binding, as I have no use for its data.
Below I decide to use it, just for the sake of demonstration.
(defun my-prompt-with-temporary-highlight-and-signal-checks-with-error-report ()
"Ask for confirmation and highlight all instances of a regexp while waiting."
(let ((regexp "(defun"))
(condition-case error-data-i-got
(progn
(highlight-regexp regexp)
(if (y-or-n-p "Should we proceed or not? ")
(user-error "You have decided to proceed; but we need to return a `user-e
(error "You prefer not to continue; but we need to return an `error'")))
(:success
(unhighlight-regexp regexp)
(message "No errors, but still need to unwind what we did, plus whatever else we
(message "The error is `%s' and its data is `%S'" (car error-data-i-got) (cdr er
(quit
(unhighlight-regexp regexp)
(message "This is our response to the user aborting the prompt")
(message "The error is `%s' and its data is `%S'" (car error-data-i-got) (cdr er
(user-error
(unhighlight-regexp regexp)
(message "This is our response to the `user-error' signal")
(message "The error is `%s' and its data is `%S'" (car error-data-i-got) (cdr er
(error
(unhighlight-regexp regexp)
(message "This is our response to the `error' signal")
(message "The error is `%s' and its data is `%S'" (car error-data-i-got) (cdr er
There will be times when unwind-protect and condition-case are the
right tools for the job. My hope is that these examples have given you the big
39
picture view and you are now ready to write your own programs in Emacs
Lisp.
40
(mapc greet-name team))))
(mapcar greet-team-and-names teams)))
(my-greet-teams
'("Pelé" "Ronaldo")
'("Maradona" "Messi")
'("Beckenbauer" "Neuer")
'("Platini" "Zidane")
'("Baresi" "Maldini")
'("Eusebio" "Cristiano Ronaldo")
'("Xavi" "Iniesta")
'("Charlton" "Shearer")
'("Puskas" "Kubala")
'("All of the Greece Euro 2004 squad ;)"))
;; => (("Pelé" "Ronaldo") ("Maradona" "Messi") ...)
The greetings are a side eect in this case and are available in the
*Messages* buer. You can quickly access that buer with C-h e (view-echo-area-messages).
It does not really matter what my-greet-teams is doing. Focus on the com-
bination of a named function and anonymous functions inside of it.
(defun my-greet-person ()
(interactive)
(message "Hello %s" (read-string "Whom to greet? ")))
41
a program, you need to write another function that does practically the same
thing except that it takes a NAME argument. Like this:
You do not need to write two separate functions which practically do the
same thing. Instead, you can have one function, with its parameters, which
decides how to get the values of the arguments passed to it depending on if
it is called interactively or programmatically. Consider this scenario:
42
(list
(read-string "Whom to greet? ")
(read-string "Where from? ")))
(message "Hello %s of %s" name country))
Write interactive specications with care and you will end up with a
rich corpus of code that is economical and exible.
18 COPYING
Copyright (C) 2025 Protesilaos Stavrou
(a) The FSF's Back-Cover Text is: You have the freedom to
copy and modify this GNU manual.
43