bbatsov / emacs-lisp-style-guide

A community-driven Emacs Lisp style guide
1.08k stars 53 forks source link

** Table of Contents :PROPERTIES: :CUSTOM_ID: table-of-contents :END:

** Source Code Layout & Organization :PROPERTIES: :CUSTOM_ID: source-code-layout-organization :END:

+BEGIN_QUOTE

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)

+END_QUOTE

+BEGIN_NOTE

The indentation guidelines specified here can be taken for granted if you're writing in Emacs. It will always do the right thing when you hit ==.

+END_NOTE

** Syntax :PROPERTIES: :CUSTOM_ID: syntax :END:

** Naming :PROPERTIES: :CUSTOM_ID: naming :END:

+BEGIN_QUOTE

The only real difficulties in programming are cache invalidation and naming things.

-- Phil Karlton

+END_QUOTE

** Macros :PROPERTIES: :CUSTOM_ID: macros :END:

** Functions :PROPERTIES: :CUSTOM_ID: functions :END:

*** Macro Declarations :PROPERTIES: :CUSTOM_ID: macro-declarations :END:

- Always declare the [[http://www.gnu.org/software/emacs/manual/html_node/elisp/Specification-List.html#Specification-List][debug-specification]], this tells edebug which arguments
  are meant for evaluation. If all arguments are evaluated, a simple
  =(declare (debug t))= is enough.

- Declare the [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Indenting-Macros.html#Indenting-Macros][indent specification]] if the macro arguments should not be
  aligned like a function (think of =defun= or =with-current-buffer=).

#+BEGIN_SRC emacs-lisp
(defmacro define-widget (name &rest forms)
  "Description"
  (declare (debug (sexp body))
           (indent defun))
  ...)
#+END_SRC

*** Loading and Autoloading :PROPERTIES: :CUSTOM_ID: loading-and-autoloading :END:

- Always end each library file with a =provide= statement and an
  appropriate comment (the =provide= statement will allow dependent
  libraries to use =require=).

#+BEGIN_SRC emacs-lisp
(provide 'foo)

;;; foo.el ends here
#+END_SRC

- Always load library dependencies with =require=, rather than =load= or
  =load-library= (the former is idempotent, while the others can result
  in multiple evaluations).

- Include =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.

#+BEGIN_SRC emacs-lisp
;;; 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)
#+END_SRC

- *Do not* provide =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.

#+BEGIN_SRC emacs-lisp
;;; good
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.foo\\'" . foo-mode))

;;; bad
;;;###autoload
(foo-setup)
#+END_SRC

** Lists :PROPERTIES: :CUSTOM_ID: lists :END:

** Comments :PROPERTIES: :CUSTOM_ID: comments :END:

+BEGIN_QUOTE

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

+END_QUOTE

*** Comment Annotations :PROPERTIES: :CUSTOM_ID: comment-annotations :END:

- Annotations should usually be written on the line immediately above
  the relevant code.

- The annotation keyword is followed by a colon and a space, then a note
  describing the problem.

- If multiple lines are required to describe the problem, subsequent
  lines should be indented as much as the first one.

- Tag the annotation with your initials and a date so its relevance can
  be easily verified.

#+BEGIN_SRC emacs-lisp
(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))
#+END_SRC

- In cases where the problem is so obvious that any documentation would
  be redundant, annotations may be left at the end of the offending line
  with no note. This usage should be the exception and not the rule.

#+BEGIN_SRC emacs-lisp
(defun bar ()
  (sleep 100)) ; OPTIMIZE
#+END_SRC

- Use =TODO= to note missing features or functionality that should be
  added at a later date.

- Use =FIXME= to note broken code that needs to be fixed.

- Use =OPTIMIZE= to note slow or inefficient code that may cause
  performance problems.

- Use =HACK= to note "code smells" where questionable coding practices
  were used and should be refactored away.

- Use =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?=

- Use other custom annotation keywords if it feels appropriate, but be
  sure to document them in your project's =README= or similar.

*** Docstrings :PROPERTIES: :CUSTOM_ID: docstrings :END:

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!

- Begin with a terse, complete sentence. Use imperative language. For
  example, prefer "Verify" over "Verifies", and "Check" over "Checks".

- When a function takes arguments, mention what the arguments do,
  whether they are required, and so on. Describe the arguments in
  UPCASE, and order them as they are used.

- Always capitalize "Emacs".

- Do not indent subsequent lines of a documentation string. This looks
  nice in the source code, but looks bizarre when users view the
  documentation.

#+BEGIN_SRC emacs-lisp
;; 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..."
  ...)

+END_SRC

** Tools :PROPERTIES: :CUSTOM_ID: tools :END:

** Existential :PROPERTIES: :CUSTOM_ID: existential :END: