Tips For Editing Lisp Code With Emacs

Xah Lee, 2007-12

This page gives you some tips about editing emacs lisp code in emacs. Most tips will also apply to editing other lisp language codes or code with many nested matching pairs. (This page presumes emacs version 22, released in mid 2007.)

Syntax Highlighting Setup

Turn on the following modes: Syntax Highlighting, Active Region Highlighting, Paren Match Highlighting.

Turn on Syntax Highlighting will color your code, Active Region Highlighting will color currently selected region, Paren Match Highlighting will color the matching parenthesis your cursor is on.

You can turn these modes on by using the menu “Options”, then “Options‣Save Options”.

Turn on CUA Mode

Turn on the CUA mode, under the menu “Options‣C-x/C-c/C-v Cut and Paste (CUA)”.

emacs “Options” menu

The CUA mode will make emacs behave more like modern applications:

Show Paren Mode

Activating the menu “Options‣Paren Match Highlighting” is equivalent to typing “Alt+x show-paren-mode”.

The “show-paren-mode” has 2 options about highlighting parens:

By default, it does (1), but the (2) seems more useful. To make it highlight entire enclosed text, put this code in your “.emacs” file:

(setq show-paren-style 'expression)

Also, you can change the default background color for highlighting the paren. You can do so by typing “Alt-x customize-face” then “show-paren-match”. Then, move your cursor to the “Attributes” section, “Background” item, in the value section type “azure2” replacing the default “turquoise”. Then, click the button “Save for Future Sessions” at the top. This will save the lisp code in your emacs init file. For example, the following shows in my emacs init file:

(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(show-paren-match ((((class color) (background light)) (:background "azure2")))))

To see a list of available colors and their names, type “Alt-x list-colors-display”.

Evaluating Lisp Expressions

To evaluate a single lisp expression, move your cursor to the right of the last closing parenthesis, and type “Alt+x eval-last-sexp” (Ctrl+x Ctrl+e). Alternatively, there's “eval-region” and “eval-buffer”.

You can also evaluate a file without opening it, by “Alt-x load-file”.

Also, emacs has a interactive emacs lisp shell, “Alt+x ielm”.

Function Documentation Lookup

Function and Variable lookup

To find the inline documentation of a function, type “Ctrl+h f” (describe-function), then type the function name. If the word the cursor is on is a valid elisp function, emacs will use that by default.

Once the function's inline doc string page comes up, you can jump to the function's location in source code by clicking on underlined file name (or move your cursor there and press Enter).

To find the documentation of a variable, type “Ctrl+h v” (describe-variable).

Finding Commands and Functions

To search command names using a regex, type “Ctrl+h a” (apropos-command). Note that this will only search commands, not functions. (A “command” is a function with the “(interactive ...)” clause.) To search all function's name space, give the command a empty argument, like this: “Ctrl+u Ctrl+h a”.

For example, you want to do something with a file but you are new to elisp and don't know what function are available. You might start looking for it by “Ctrl+h a RET file” which will return all functions with “file” in their name.

To search all symbols space (function, commands, variables), type “Alt+x apropos”.

Note: all these commands are under the menu “Help‣Describe” or “Help‣Search Documentation”.

Searching Elisp Manual

Usually a function's inline documentation is sufficient for knowing how to use the function, but sometimes you want to read about a function in the elisp manual, which often gives more detail and context.

To search for a function in the elisp manual, use “elisp-index-search”. For emacs manual, use “emacs-index-search”. Both of these commands are under the menu “Help”.

Alternatively, you can use google to seach a site of the elisp manual in html. See the Google search box at the bottom of GNU Emacs Lisp Reference Manual.

Navigating Nested Code

Lisp code with its nested parenthesis syntax represents a tree structure. Emacs has several commands that are very helpful in moving around nested syntax, analogous to navigating a tree. Here is a table showing their shortcuts, names, and purpose. (For historical reasons, lisp code units are sometimes called “sexp”, short for “S-EXPression” (the “S” denote “Symbolic”).)

Shortcut Command name Purpose
Ctrl+Meta+← backward-sexp Move to previous sibling
(move to the (beginning of) previous sexp unit)
Ctrl+Meta+→ forward-sexp Move to next sibling
(move to the (end of) next sexp unit)
Ctrl+Meta+↑ backward-up-list Move to parent
(move to the (beginning of) outer paren pair)
Ctrl+Meta+↓ down-list Move to first child
(move into the (beginning of) first inner paren pair)

Here is a lisp code layed out in a way to show its tree structure. You should try the above commands on it. It is very helpful to understand how sexp corresponds to a tree, and how the commands move the cursor exactly.

( defun
  fold
  "Applies (f x ele) recursively to the list li ..."
  (f x li)
  ( let
    (
      (li2 li)
      (ele)
      (x2 x)
    )
    (while
      (setq ele (pop li2))
      (setq x2 (funcall f x2 ele))
    )
    x2
  )
)

Place your cursor at the beginning of the left bracket. Now, try to move your cursor, by using only the tree-walking commands, to the “pop”, then move it to “let”, then “funcall”.

Moving To Previous/Next Sibling That Has Children

The commands “backward-sexp” and “forward-sexp” moves to the previous or next sibling of the sexp the current cursor is on. For example, if you have “(a (b) c d (e f))” and your cursor is on end paren of “(b)”, then, “forward-sexp” will move it to “c”, and do it again will move it to “d”, then end paren of “(e f)”. Occationally, you want to move to the next sibling that has a branch. For example, if you are at the end of “(b)” and you want to move it to next paren pair the “(e f)”, you can then use “backward-list” and “forward-list”.

Selecting A Sexp Unit

You can use the command “mark-sexp” (Ctrl+Meta+Space) to select a complete sexp. However, your cursor should be at the beginning of the unit. To select a complete sexp, type “Ctrl+Alt+↑” then “Ctrl+Alt+Space”.

Typing Conveniences

These commands's default shortcuts are difficult to press. You can rebind them like this:

Rebind Tree-Walkers

The default shortcut for tree-walking commands involves holding down both Meta and Control, which is inconvenient. You can rebind them with just Meta.

(global-set-key (kbd "M-<up>") 'backward-up-list)
(global-set-key (kbd "M-<down>") 'down-list)
(global-set-key (kbd "M-<left>") 'backward-sexp)
(global-set-key (kbd "M-<right>") 'forward-sexp)

The above binding will make “Meta+‹arrow›” for walking the sexp tree.

Typing Parenthesis By Pairs

On a PC keyboard, the matching pairs “(){}[]” are in locations difficult to type. It is convenient to move them under the home row, and also to type them always in pairs. Here's the code you can use for that:

(global-set-key (kbd "H-j") (lambda () (interactive) (insert "{}") (backward-char 1)))
(global-set-key (kbd "H-k") (lambda () (interactive) (insert "()") (backward-char 1)))
(global-set-key (kbd "H-l") (lambda () (interactive) (insert "[]") (backward-char 1)))

The “H” in the above code indicates the Hyper modifier key. You can make your Mac's Option key as Hyper or your PC's Windows key. Here's the code:

(setq w32-pass-lwindow-to-system nil 
      w32-lwindow-modifier 'hyper)  ;; Left Windows key 
(setq mac-option-modifier 'hyper)

Arrows Keys Under Homerow

Emacs default cursor movement keys (C-p, C-n, C-b, C-f) are inconvenient to press. (See Why Emacs's Keyboard Shortcuts Are Painful) You can rebind the cursor movement keys under the home row with “Alt” down. Here's the code:

(global-set-key (kbd "M-j") 'backward-char) ; was indent-new-comment-line
(global-set-key (kbd "M-l") 'forward-char)  ; was downcase-word
(global-set-key (kbd "M-i") 'previous-line) ; was tab-to-tab-stop
(global-set-key (kbd "M-k") 'next-line) ; was kill-sentence

(global-set-key (kbd "M-SPC") 'set-mark-command) ; was just-one-space

For a more radical ergonomic shortcut set, see: A Ergonomic Keyboard Shortcut Layout.


Related essays:


Page created: 2007-12.
© 2007 by Xah Lee.
Xah Signet