Role models are important.
– Officer Alex J. Murphy / RoboCop
This Emacs Lisp style guide recommends best practices so that real-world Emacs Lisp programmers can write code that can be maintained by other real-world Emacs Lisp programmers. A style guide that reflects real-world usage gets used, and a style guide that holds to an ideal that has been rejected by the people it is supposed to help risks not getting used at all — no matter how good it is.
The guide is separated into several sections of related rules. I’ve tried to add the rationale behind the rules (if it’s omitted, I’ve assumed that it’s pretty obvious).
I didn’t come up with all the rules out of nowhere; they are mostly based on my extensive experience of using Emacs and creating/maintaining Emacs packages, feedback and suggestions from members of the Emacs Lisp community, and various highly regarded Emacs Lisp programming resources, such as “GNU Emacs Lisp Reference Manual”.
The guide is still a work in progress; some sections are missing, others are incomplete, some rules are lacking examples, some rules don’t have examples that illustrate them clearly enough. In due time these issues will be addressed — just keep them in mind for now.
Please note, that the Emacs developers maintain a list of coding conventions and tips too.
You can generate a PDF or an HTML copy of this guide using Pandoc.
Nearly everybody is convinced that every style but their own is ugly and unreadable. Leave out the “but their own” and they’re probably right…
– Jerry Coffin (on indentation)
In practical terms this means you should add the following to your Emacs config:
(setq-default indent-tabs-mode nil)
An even better idea would be to force the use of spaces using .dir-locals.el
in each of your Emacs Lisp projects.
((emacs-lisp-mode (indent-tabs-mode nil)))
;; good (format "%s %d" something something-else) ;; bad (format "%s %d" something something-else)
;; good (format "%s %d" something something-else) ;; bad (format "%s %d" something something-else)
let
, with-current-buffer
, etc. The special arguments should either be on the same line as the form’s name or be indented by 4 spaces. The body arguments should be indented by 2 spaces.
;; good (when something (something-else)) ;; bad - four spaces on the body (when something (something-else)) ;; bad - aligned like a regular function (when something (something-else))
if
form is a special argument, indent it by 4 spaces.
;; good (if something then-clause else-clause) ;; bad (if something then-clause else-clause)
let
bindings.
;; good (let ((thing1 "some stuff") (thing2 "other stuff")) ...) ;; bad (let ((thing1 "some stuff") (thing2 "other stuff")) ...)
bash$ git config --global core.autocrlf true
(
, {
and [
) or follows a closing bracket()
, }
and ]
), separate that text from that bracket with a space. Conversely, leave no space after an opening bracket and before following text, or after preceding text and before a closing bracket.
;; good (foo (bar baz) quux) ;; bad (foo(bar baz)quux) (foo ( bar baz ) quux)
;; good; single line (when something (something-else)) ;; bad; distinct lines (when something (something-else) )
;; good (defvar x ...) (defun foo ...) ;; bad (defvar x ...) (defun foo ...)
An exception to the rule is the grouping of related =def=s together.
;; good (defconst min-rows 10) (defconst max-rows 20) (defconst min-cols 15) (defconst max-cols 30)
cond
.;;; -*- lexical-binding: t; -*-
if
in a progn
(it’s wrapped in progn
implicitly).;; good (if something if-clause (something) (something-else)) ;; bad (if something if-clause (progn (something) (something-else)))
when
instead of (if ... (progn ...)
.;; good (when pred (foo) (bar)) ;; bad (if pred (progn (foo) (bar)))
unless
instead of (when (not ...) ...)
.;; good (unless pred (foo) (bar)) ;; bad (when (not pred) (foo) (bar))
not
instead of null
, unless your checking whether something is nil
(empty list). Despite its name (null
instead of nullp
), the function null
is mostly meant to be used as a predicate.;; good (if (null lst) ...) (if (or (not foo) something) ...) ;; bad (if (not lst)) (if (and (null foo) bar) ...)
<
, >
, etc. accept a variable number of arguments as of Emacs 24.4.;; Preferred (< 5 x 10) ;; Old (and (> x 5) (< x 10))
t
as the catch-all test expression in cond
.;; good (cond ((< n 0) "negative") ((> n 0) "positive") (t "zero")) ;; bad (cond ((< n 0) "negative") ((> n 0) "positive") (:else "zero"))
(1+ x)
& (1- x)
instead of (+ x 1)
and (- x 1)
.with-eval-after-load
instead of eval-after-load
.;; good (with-eval-after-load "foo" (bar) (baz)) ;; bad (eval-after-load "foo" '(progn (bar) (baz)))
The only real difficulties in programming are cache invalidation and naming things.
– Phil Karlton
lisp-case
for function and variable names.;; good (defvar some-var ...) (defun some-fun ...) ;; bad (defvar someVar ...) (defun somefun ...) (defvar some_fun ...)
;; good (defun projectile-project-root ...) ;; bad (defun project-root ...)
_
.;; good (lambda (x _y) x) ;; bad (lambda (x y) x)
--
to denote private top-level definitions (e.g. =projectile–private-fun=).p
if it’s a single-word name and a -p
if it’s a multi-word name (e.g., evenp
and buffer-live-p
).;; good (defun palindromep ...) (defun only-one-p ...) ;; bad (defun palindrome? ...) ; Scheme style (defun is-palindrome ...) ; Java style
-face
.;; good (defface widget-inactive ...) ;; bad (defface widget-inactive-face ...)
;;; Good (mapcar (lambda (x) (or (car x) "")) some-list) (let ((predicate (lambda (x) (and (numberp x) (evenp x))))) (funcall predicate 1000)) ;;; Bad - Define real functions for these. (defcustom my-predicate (lambda (x) (and (numberp x) (evenp x))) ...) (define-key my-keymap (kbd "C-f") (lambda () (interactive) (forward-char 1))) (add-hook 'my-hook (lambda () (save-some-buffers)))
;;; Good (lambda (x) (car x)) ;;; Ok, but redundant. #'(lambda (x) (car x)) ;;; Bad '(lambda (x) (car x))
;; good (cl-remove-if-not #'evenp numbers) ;; bad (cl-remove-if-not (lambda (x) (evenp x)) numbers)
#'
) when quoting function names. It’s a good hint for the byte-compiler, which will warn you if the function is undefined. Some macros can also behave differently otherwise (like cl-labels
).;; good (cl-remove-if-not #'evenp numbers) (global-set-key (kbd "C-l C-l") #'redraw-display) (cl-labels ((butterfly () (message "42"))) (funcall #'butterfly)) ;; bad (cl-remove-if-not 'evenp numbers) (global-set-key (kbd "C-l C-l") 'redraw-display) (cl-labels ((butterfly () (message "42"))) (funcall 'butterfly))
(declare (debug t))
is enough.defun
or with-current-buffer
).(defmacro define-widget (name &rest forms) "Description" (declare (debug (sexp body)) (indent defun)) ...)
provide
statement and an appropriate comment (the provide
statement will allow dependent libraries to use require
).(provide 'foo) ;;; foo.el ends here
require
, rather than load
or load-library
(the former is idempotent, while the others can result in multiple evaluations).autoload
cookies for mode definitions and commonly-used user-facing functions and commands (i.e. setup functions and commands that could be bound to a key). Conversely, do not provide autoload cookies for global variables or internal functions.;;; good ;;;###autoload (define-derived-mode foo-mode ...) ;;;###autoload (define-minor-mode foo-minor-mode ...) ;;;###autoload (defun foo-setup () ...) ;;; bad ;;;###autoload (defun foo--internal () ...) ;;;###autoload (defvar foo-option)
autoload
cookies for non-definition top-level forms (autoloading a library should never alter the behavior of a user’s configuration). The single exception: auto-mode-alist
can be altered for new major modes.;;; good ;;;###autoload (add-to-list 'auto-mode-alist '("\\.foo\\'" . foo-mode)) ;;; bad ;;;###autoload (foo-setup)
dolist
instead of calling the same s-exps over different variables:;;; good (dolist (hook '(prog-mode-hook text-mode-hook)) (add-hook hook 'turn-on-column-number-mode) (add-hook hook 'turn-off-line-number-mode) (add-hook hook 'linum-mode)) ;;; bad (add-hook 'prog-mode-hook 'turn-on-column-number-mode) (add-hook 'prog-mode-hook 'turn-off-line-number-mode) (add-hook 'prog-mode-hook 'linum-mode) (add-hook 'text-mode-hook 'turn-on-column-number-mode) (add-hook 'text-mode-hook 'turn-off-line-number-mode) (add-hook 'text-mode-hook 'linum-mode)
seq-do
or dolist
instead of mapcar
if you don’t intend to concatenate the result.;;; good (font-lock-add-keywords nil (mapcar 'downcase list-of-crazy-cased-words)) (seq-do 'load list-of-files-to-load) ;;; bad (mapcar 'load list-of-files-to-load)
dolist
instead of calling seq-do
over a lambda. Reserve seq-do
for single function calls.;;; good (dolist (map (list c-mode-map c++-mode-map)) (define-key map "\C-c\C-c" 'compile)) ;;; bad (mapc (lambda (map) (define-key map "\C-c\C-c" 'compile)) (list c-mode-map c++-mode-map))
Good code is its own best documentation. As you’re about to add a comment, ask yourself, “How can I improve the code so that this comment isn’t needed?” Improve the code and then document it to make it even clearer. – Steve McConnell
;;; Frob Grovel ;; This is where Frob grovels and where Grovel frobs. ;; This section of code has some important implications: ;; 1. Foo. ;; 2. Bar. ;; 3. Baz. (defun fnord (zarquon) ;; If zob, then veeblefitz. (quux zot mumble ; Zibblefrotz. frotz))
;; bad (1+ counter) ; increments counter by one
Good code is like a good joke - it needs no explanation. – Russ Olsen
Do, or do not. There is no try. – Yoda
(defun some-fun () ;; FIXME: This has crashed occasionally since v1.2.3. It may ;; be related to the BarBazUtil upgrade. (xz 13-1-31) (baz))
(defun bar () (sleep 100)) ; OPTIMIZE
TODO
to note missing features or functionality that should be added at a later date.FIXME
to note broken code that needs to be fixed.OPTIMIZE
to note slow or inefficient code that may cause performance problems.HACK
to note “code smells” where questionable coding practices were used and should be refactored away.REVIEW
to note anything that should be looked at to confirm it is working as intended. For example: REVIEW: Are we sure this is how the client does X currently?
README
or similar.Emacs is famous for the breadth, depth, and ubiquity of its documentation. By taking the time to write docstrings in your package, you are helping to continue that tradition!
;; good (defun goto-line (line &optional buffer) "Go to LINE, counting from line 1 at beginning of buffer. If called interactively, a numeric prefix argument specifies LINE; without a numeric prefix argument, read LINE from the minibuffer..." ...) ;; bad (defun goto-line (line &optional buffer) "Go to LINE, counting from line 1 at beginning of buffer. If called interactively, a numeric prefix argument specifies LINE; without a numeric prefix argument, read LINE from the minibuffer..." ...) ;; also bad (defun goto-line (line &optional buffer) "Go to LINE, counting from line 1 at beginning of buffer. If called interactively, a numeric prefix argument specifies LINE; without a numeric prefix argument, read LINE from the minibuffer..." ...)
checkdoc
to check for docstring style issues.
checkdoc
with Flycheck.package-lint
README about integration with Flycheck.Nothing written in this guide is set in stone. It’s my desire to work together with everyone interested in Emacs Lisp coding style, so that we could ultimately create a resource that will be beneficial to the entire Emacs community.
Feel free to open tickets or send pull requests with improvements. Thanks in advance for your help!
This work is licensed under a Creative Commons Attribution 3.0 Unported License
A community-driven style guide is of little use to a community that doesn’t know about its existence. Tweet about the guide, share it with your friends and colleagues. Every comment, suggestion or opinion we get makes the guide just a little bit better. And we want to have the best possible guide, don’t we?
Cheers,
Bozhidar
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4