How-to: Emacs Lisp
Emacs Lisp, a a dialect of Lisp used in GNU Emacs, the programmable text editor. On this page, I collect solutions to the problems I've encountered when programming in Emacs Lisp, and other related information.
Table of Contents
- Remove function from a hook after it runs once
- Ensure a form runs with lexical binding
- Convert a keyword to regular symbol and vice-versa
- Insert read-only text into buffer
- Convert read-only text in buffer to editable text
- Find parents of mode
- Check if a symbol is available in default Emacs session
- Use lexical binding
Remove function from a hook after it runs once
It's easy to remove a named function from a hook at any given time, since it can always be uniquely identified. But whatever the function is doing might not be significant enough to warrant a name and polluted obarray
.
To be able to remove the function after it runs while avoiding the obarray
pollution, we can use a trick involving an uninterned symbol stored in a variable.
NOTE: This requires lexical binding to be enabled, due to embedding of function
inside a lambda, which creates a closure. See Use lexical binding for information on how to enable lexical binding.
(defun add-hook-autoremove (hook function &optional depth local)
"Add FUNCTION to HOOK.
FUNCTION will automatically remove itself from HOOK after running once.
DEPTH and LOCAL are the same as in `add-hook', which see.
Requires lexical binding.
Example calls:
(add-hook-autoremove 'org-mode-hook 'org-toggle-link-display)"
;; Argument validation:
;; The only argument we need to validate is FUNCTION, because it's
;; not validated in `add-hook', and by the time it's `funcall'ed,
;; it will be already too late for recovery.
(or (functionp function)
(signal 'wrong-type-argument (list 'functionp function)))
;; Code:
;; Normally, uninterned symbols with the same name are not `eq',
;; but in this case we will be using a reference to the same
;; uninterned symbol, so there is no problem.
(let ((fn (make-symbol "FN")))
;; Since `symbol-function' is itself a function, FN will get
;; evaluated and what will be passed is the symbol it holds. It's
;; the same for `add-hook' and `remove-hook'.
(setf (symbol-function fn)
(lambda () (funcall function) (remove-hook hook fn local)))
(add-hook hook fn depth local)))
Ensure a form runs with lexical binding
Note: For instructing Emacs in general to use lexical binding see Use lexical binding.
TL;DR: Use eval
with its optional LEXICAL
argument set to t
.
When lexical binding was introduced in Emacs 24.1, the eval
function was also changed accordingly to accept a LEXICAL
parameter. If LEXICAL
is non-nil, eval
will run as if lexical-binding
is enabled.
Therefore, by simply wrapping a form in a call to eval
, we can ensure lexical execution context regardless of the environment.
;;; Even though lexical-binding is nil,
;;; this produces a closure regardless.
(eval
(let ((x "hello"))
(lambda () (message x)))
t)
It's useful to wrap this in a slightly more wieldy macro:
(defmacro with-lexical-binding (&rest body)
"Evaluate BODY in lexical environment.
Every variable to be lexically scoped needs to be defined in BODY, as
environment outside the call to this macro cannot be accessed."
;; Tell the debugger that all arguments are evaluated, and that
;; there are no distinguished arguments (everything is part of the
;; body.)
(declare (debug t) (indent 0))
`(eval '(progn ,@body) t))
This is of course subject to the usual perils of eval
, the main one of which is that it has no access to the surrounding lexical environment. Make sure to properly include all the forms creating the lexical variables you use inside the macro's body.
Convert a keyword to regular symbol and vice-versa
In Emacs Lisp, keywords are symbols whose name starts with a colon (:
). Unlike symbols, keywords are constant, and cannot be assigned a value.
To convert from symbol to keyword and the other way around, we simply need to add or remove leading colons from the name as appropriate.
(defun keyword-to-symbol (keyword)
"Turn a :keyword into 'keyword.
Example calls:
(keyword-to-symbol :::test) => 'test"
;; Handle :symbol, ::symbol, etc.
(intern (string-trim-left (symbol-name keyword) "^:+")))
(defun symbol-to-keyword (symbol)
"Turn a 'symbol into :symbol.
Example calls:
(symbol-to-keyword 'test) => :test"
(if (keywordp symbol)
symbol
;; Add colon as the first character,
;; which turns the name into a keyword name.
(intern (concat ":" (symbol-name symbol)))))
Insert read-only text into buffer
When a string with non-nil read-only
property is inserted into a buffer, Emacs will not allow it to be changed. This can be useful when we don't want the user to remove some text for whatever reason.
See the Emacs manual for more information about text properties.
(defun insert-read-only (&rest args)
"Insert ARGS into the buffer with the read-only property.
See `insert' for more information.
Example calls:
(insert-read-only)
(insert-read-only \"Emacs\" \"Lisp\")"
(let ((inhibit-read-only t))
(insert (propertize (apply #'concat args) 'read-only t))))
Convert read-only text in buffer to editable text
There is a trick to removing read-only
property from already-inserted text; inhibit-read-only
needs to be bound to t
, so that Emacs allows us to modify (in this case remove property) from that text.
(defun remove-read-only (start end &optional buffer)
"Remove the read-only property from text between START and END.
BUFFER defaults to the current buffer.
Example calls:
(remove-read-only (point-min) (point-max))"
(with-current-buffer (or buffer (current-buffer))
(let ((inhibit-read-only t))
(remove-list-of-text-properties start end '(read-only)))))
Find parents of mode
Note: Emacs 30.1 added derived-mode-all-parents
, which I recommend using instead. But it's important to note two caveats:
- The returned list contains at least
mode
, so you should usecdr
on the result to get just the parents. - The returned list is not freshly consed, and should not be modified. If you want to modify the result, make a copy.
copy-sequence
should do, as the returned list should be flat.
Newly created Emacs modes can inherit from already existing ones via define-derived-mode
. Among other things, this sets the derived-mode-parent
property on the derived mode's symbol to whatever mode it inherited from. We can use this property to get the inheritance list for mode
.
If the mode has not been loaded yet, information about its parents will not be available. In that case you can specify try-loading
to try autoloading the mode, and then its parents recursively.
(defun derived-mode-all-parents (mode &optional try-loading)
"Return a list of MODE's parents.
This works if MODE was defined using `define-derived-mode'.
TRY-LOADING will try to recursively autoload modes to get
more accurate information on their parents.
(derived-mode-all-parents 'org-mode) => (outline-mode text-mode)"
;; Argument validation. Can be removed for performance.
(or (and (symbolp mode) (not (keywordp mode)))
(error "Wrong type argument: expected symbol, got %s" (type-of mode)))
(let* ((loader (lambda (mode try-loading)
(let ((parent (get mode 'derived-mode-parent)))
(or
;; No need to do any loading
;; if we can find the parent.
parent
(and try-loading
(let ((file (symbol-file mode)))
(and file
;; Autoloaded mode will probably
;; have relative path; if it's
;; absolute, it was either already
;; loaded or not autoloaded in the
;; first place.
(not (file-name-absolute-p file))
(require (intern file) nil t)
(get mode 'derived-mode-parent))))))))
(parent (funcall loader mode try-loading))
;; No particular reason for putting mode as the car in cons.
;; It could have very well been nil instead, but this makes
;; the function easier to modify in case we want to return
;; a list where mode is in car and its parents in cdr.
(head (and parent (cons mode nil)))
(tail head))
(while parent
(let ((el (cons parent nil)))
(setcdr tail el)
(setq tail el))
(setq parent (funcall loader parent try-loading)))
(cdr head)))
Check if a symbol is available in default Emacs session
When writing Emacs Lisp, I like to depend only on what's available out of the box. Even better if it's something that doesn't need a require
(so parts of the C core, preloaded lisp, and to lesser extent autoloaded code.)
This piece of code provides a way to check whether the symbol is a part of:
- C code, which is ideal.
- Preloaded lisp code like
subr.el
, which doesn't needrequire
and shouldn't load additional code. - Autoloaded functions, which also don't need
require
but load additional code. - Lisp code that's included in the default installation but needs explicit
require
to be used.
(defun find-symbol-origin (symbol)
"Check if SYMBOL is available and where.
Should be called in a fresh emacs -Q instance."
(or (let* ((file (and lisp-directory (symbol-file symbol nil)))
;; .elc files in preloaded-file-list are saved without extension.
(file-normalized (and file (if (string-equal (file-name-extension file) "elc")
(file-name-sans-extension file)
file)))
(remove-prefix (and file-normalized
;; Define string-remove-prefix to avoid pulling subr-x.el.
(lambda (prefix string)
(if (string-prefix-p prefix string)
(substring string (length prefix))
string))))
(preloaded (and file-normalized
;; Files in preloaded-file-list are stored as a path relative
;; to lisp-directory, while our path is absolute.
;; We correct that here.
(member (funcall remove-prefix lisp-directory file-normalized)
preloaded-file-list))))
(and preloaded (message "Preload")))
;; If it's not preloaded, but still has a file associated with
;; it, it must be available via autoload. This will return nil
;; for functions/symbols defined in C.
(and (symbol-file symbol nil) (message "Autoload"))
(and (help-C-file-name symbol 'var) (message "C variable"))
(and (help-C-file-name symbol 'subr) (message "C function"))
;; Search definition-prefixes directly for a matching
;; prefix. This gives a list of features where this symbol could
;; be defined. It would be possible to then load these features
;; and determine where exactly the symbol is coming from, like
;; `help-fns.el' does, but I'm okay with this.
(let* ((name (symbol-name symbol))
(len (length name))
(pkgs nil))
(while (and (null pkgs) (> len 0))
(setq pkgs (gethash (substring name 0 len) definition-prefixes))
(setq len (1- len)))
(when pkgs
(message "Require %s" pkgs)))
(message "Unknown")))
There is a caveat; this code should be run in a fresh instance of Emacs to work as intended. Emacs state is a complex web of dependencies, and loading just one file can cause important variables to become truncated, or additional features to be pulled. A good example is the help facility in help-fns.el
that does the following by default if you C-h f s <TAB>
(let ((prefixes (radix-tree-prefixes (help-definition-prefixes) string)))
(help--load-prefixes prefixes)))
This seemingly innocuous piece of code eventually causes definition-prefixes
to be completely cleared out.
It's easy to ensure a fresh environment by running Emacs in batch mode through a shell script:
#!/bin/bash
# ./script "<sym1>" ... "<symn>"
# Checks if each symbol is available in default Emacs session, and where it came from.
function check() {
# check <symbol>
emacs -Q --batch --eval "(defun find-symbol-origin (symbol) (or (let* ((file (and lisp-directory (symbol-file symbol nil))) (file-normalized (and file (if (string-equal (file-name-extension file) \"elc\") (file-name-sans-extension file) file))) (remove-prefix (and file-normalized (lambda (prefix string) (if (string-prefix-p prefix string) (substring string (length prefix)) string)))) (preloaded (and file-normalized (member (funcall remove-prefix lisp-directory file-normalized) preloaded-file-list)))) (and preloaded (message \"Preload\"))) (and (symbol-file symbol nil) (message \"Autoload\")) (and (help-C-file-name symbol 'var) (message \"C variable\")) (and (help-C-file-name symbol 'subr) (message \"C function\")) (let* ((name (symbol-name symbol)) (len (length name)) (pkgs nil)) (while (and (null pkgs) (> len 0)) (setq pkgs (gethash (substring name 0 len) definition-prefixes)) (setq len (1- len))) (when pkgs (message \"Require %s\" pkgs))) (message \"Not found\")))" --eval "(find-symbol-origin '$1)" 2>&1
}
# Get the length of the longest symbol.
OFFSET="$(printf '%s\n' "$@" | awk '{l=length($0)}l>max{max=l}END{print max}')"
until [ $# -le 0 ]
do
# Print <symbol> - <type>.
printf "%-${OFFSET}s - %s\n" "$1" "$(check "$1")"
shift 1
done
Use lexical binding
TL;DR: Run the elisp-enable-lexical-binding
command to ensure lexical binding is used in the current buffer.
As of 2024, dynamic binding is still used by default outside of some specific contexts. If the user wants to use lexical binding (added in 24.1, released on 2012-06-10,) they need to specify it.
Use of lexical binding is controlled by the lexical-binding
variable, which is buffer-local. While it might be tempting to give it a default value of t
, I would not recommend doing that; some packages might still depend on dynamic binding being the default, and you might find that your code is not working when run by other people who use the default value of nil
.
The best way to set the lexical-binding
variable is on per buffer/file basis by using file variables. Assuming you have not manipulated the value of lexical-binding
manually, an invocation of the elisp-enable-lexical-binding
command should set lexical-binding
to t
in the current buffer, and also add the appropriate file variable entry. It's important to note that setting lexical-binding
as a file variable works only on the first line in the buffer. This is because eval needs to know what mode of operation to use before it starts evaluating.
For the same reason, it is impossible to set lexical-binding
from Lisp code being evaluated and have it work correctly, unless it is already a lexical execution context (*scratch*
or *ielm*
buffer, M-:
aka eval-expression
, or code ran via the eval command-line option.)
Thus, assuming that lexical-binding
is initially nil
, it will stay nil
in all of the following examples (at least for duration of the form evaluation):
(progn
(setq lexical-binding t)
;; Even though lexical-binding is now t, the following will not
;; create a closure:
(let ((x "hello"))
(lambda () (message x))))
;;; Start of test-lexical.el
;;; This is essentially a no-op.
(setq lexical-binding t)
;; Even though lexical-binding is now t, the following will not
;; create a closure:
(let ((x "hello"))
(lambda () (message x)))
;;; End of test-lexical.el
;;; lexical-binding will not change after `load'.
(load "test-lexical.el")
If for some reason you can't guarantee lexical-binding
will be enabled, but still need to ensure a form will run in lexical context, you can use a hack involving eval
. See Ensure a form runs with lexical binding for more information.
See also: 12.10.4 Using Lexical Binding in the GNU Emacs manual.