[slime-devel] marco's highly opiniated guide to editing lisp code in emacs

Marco Baringer mb at bese.it
Sun Jul 18 11:41:07 UTC 2004


[it would be nice if someone would point out the "proper" place for
this info. i was thinking somewhere on the emacswiki, but i couldn't
figure out where.]

[i suck at speling]

emacs is a very large program, i've been using it as a mail reader,
shell, note pad and ide for the past 6 years and i'm still learning
new things. for those of you who've never really sat down and looked
at emacs' lisp editing functions, here's a guide to what i do.

0a - do not program with western european keyboards layouts, accented
chars are used very rarely in code, while puncuation chars are far
more common. 

0b - learn to touch type.

0c - don't spend 1500 EUR (or more) for a computer and then skimp on
the keyboard.

1 - in just about every programming language i've ever used (C, Java,
perl, bash and lisp), and in normal prose, paranthesis occur for more
often that the square bracket chars. so the first thing to do is to
make typing the parenthesis easy:

(keyboard-translate ?\( ?\[)
(keyboard-translate ?\[ ?\()
(keyboard-translate ?\) ?\])
(keyboard-translate ?\] ?\))

This assumes that the [ and ] are easy to reach unshifted chars.

2 - learn to use structured editing. what the hell is structured
editing? it's just a way of working which attempts, as much as is
usefull, to keep the source code consistently valid. this means,
among other things, that you inserting an open parenthesis should
insert the closing one as well (idem for ").

here's my setup:

(defun se:move-past-close-and-just-one-space ()
  "Move past next `)' and ensure just one space after it. If called
  repeatedly removes any leading white space and moves up the enclosing
  list.

Copyright (c) 2002--2003 Hannu Koivisto <azure at iki.fi>.  All rights
reserved.  License: GPL."
  (interactive)
  (when (eq 'se:move-past-close-and-just-one-space last-command)
    (delete-horizontal-space t))
  (up-list)
  (just-one-space)
  (setq this-command 'se:move-past-close-and-just-one-space))

(define-key slime-mode-map [(?\()] 'insert-parentheses)
(define-key slime-mode-map [(?\))] 'se:move-past-close-and-just-one-space)
(define-key slime-mode-map [(return)] 'newline-and-indent)

2b - so how do you insert a simple, stand alone, ( or ) char? i
happen to have the = char directly below ) and the \ char just to the
left of that, so here's what i use:

(define-key slime-mode-map [(control ?\=)] (lambda () (interactive) (insert "(")))
(define-key slime-mode-map [(control ?\\)] (lambda () (interactive) (insert ")")))

chose something that makes sense to use and which requires at most one
key press (though it will probably be modified). even with structured
editing you'll still need to insert lone parenthesis every now and
then.

2c - if you don't want to be so radical and change the meaning of (
and ) know that, in the default lisp mode, insert-parentheses is bound
to M-( and M-) is bound to move-past-close-and-reindent.

3 - when editing lisp code you generally don't work on a line or
character basis, but you work by moving around lists. usefull emacs
functions:

  transpose-sexps - this simply swaps the sexp before point with
      the sexp after point.

  backward-sexp - mave to before the preceding sexp

  forward-sexp - move after the following sexp

  kill-sexp - kill the following sexp

  backward-up-list - move point to the start of the enclosing list.

  up-list - move forward out of the current enclosing list

  down-list - move the just after the open paren of the next list

  all of these functions take prefix args which specify how many sexps
  to move around. backward-up-list can take a negative arg which maves
  out of the enclosing parens but forwards, basically simulating
  up-list.

when editing lisp code you will use these the
forward/backward/transpose-sexp functions far more often the the char
versions, i'd suggest swapping them so that C-M-t (transpose-sexp) is
C-t and C-t (transpose-char) is C-M-t:

(define-key slime-mode-map (kbd "C-t") 'transpose-sexps)
(define-key slime-mode-map (kbd "C-M-t") 'transpose-chars)
(define-key slime-mode-map (kbd "C-b") 'backward-sexp)
(define-key slime-mode-map (kbd "C-M-b") 'backward-char)
(define-key slime-mode-map (kbd "C-f") 'forward-sexp)
(define-key slime-mode-map (kbd "C-M-f") 'forward-char)

on a dvorak keyboard the htn keys are located directly under the right
hand, so i've gone ahead and put the forward functions on n, on the
transpose ones on t and the backward ones on h.

4 - i often want to "wrap" a sexp with more code, when for example, i
want to add a let binding to a part of code. fortunetly the
insert-parentheses function when given a prefix argument will insert
the open paren at point and the clasing paren after the following n
sexps. so if we have the point before "(foo) (bar)" then simply typing
M-2 ( will give us "((foo) (bar))" with the point directly after the
first (newly inserted) paren.

5 - i often also decide to unwrap same part of code (if for example
i've decided to remove a let and it's bindings around a particular
form.

(defun mb:unwrap-next-sexp ()
  "Convert (x ...) to ..."
  (interactive)
  (forward-sexp)
  (backward-delete-char 1)
  (backward-up-list)
  (delete-char 1)
  (kill-sexp)
  (lisp-indent-line))

6 - I am lucky enough to spend much of my time programming lisp, so i
find these changes worth the effort it took to figure them out and get
used to them. YMMV.

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen




More information about the slime-devel mailing list