Nitpicking change of format from ISBN-1882114-43-4 to ISBN 1-882114-43-4
[emacs.git] / lisp / emacs-lisp / cl-indent.el
blob294b7b7f5d2150a7a94d26842fb302a3caae7c37
1 ;;; cl-indent.el --- enhanced lisp-indent mode
3 ;; Copyright (C) 1987, 2000, 2001 Free Software Foundation, Inc.
5 ;; Author: Richard Mlynarik <mly@eddie.mit.edu>
6 ;; Created: July 1987
7 ;; Maintainer: FSF
8 ;; Keywords: lisp, tools
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
27 ;;; Commentary:
29 ;; This package supplies a single entry point, common-lisp-indent-function,
30 ;; which performs indentation in the preferred style for Common Lisp code.
31 ;; To enable it:
33 ;; (setq lisp-indent-function 'common-lisp-indent-function)
35 ;;>> TODO
36 ;; :foo
37 ;; bar
38 ;; :baz
39 ;; zap
40 ;; &key (like &body)??
42 ;; &rest 1 in lambda-lists doesn't work
43 ;; -- really want (foo bar
44 ;; baz)
45 ;; not (foo bar
46 ;; baz)
47 ;; Need something better than &rest for such cases
49 ;;; Code:
51 (defgroup lisp-indent nil
52 "Indentation in Lisp"
53 :group 'lisp)
56 (defcustom lisp-indent-maximum-backtracking 3
57 "*Maximum depth to backtrack out from a sublist for structured indentation.
58 If this variable is 0, no backtracking will occur and forms such as flet
59 may not be correctly indented."
60 :type 'integer
61 :group 'lisp-indent)
63 (defcustom lisp-tag-indentation 1
64 "*Indentation of tags relative to containing list.
65 This variable is used by the function `lisp-indent-tagbody'."
66 :type 'integer
67 :group 'lisp-indent)
69 (defcustom lisp-tag-body-indentation 3
70 "*Indentation of non-tagged lines relative to containing list.
71 This variable is used by the function `lisp-indent-tagbody' to indent normal
72 lines (lines without tags).
73 The indentation is relative to the indentation of the parenthesis enclosing
74 the special form. If the value is t, the body of tags will be indented
75 as a block at the same indentation as the first s-expression following
76 the tag. In this case, any forms before the first tag are indented
77 by `lisp-body-indent'."
78 :type 'integer
79 :group 'lisp-indent)
82 (defvar lisp-indent-error-function)
83 (defvar lisp-indent-defun-method '(4 &lambda &body))
85 ;;;###autoload
86 (defun common-lisp-indent-function (indent-point state)
87 (let ((normal-indent (current-column)))
88 ;; Walk up list levels until we see something
89 ;; which does special things with subforms.
90 (let ((depth 0)
91 ;; Path describes the position of point in terms of
92 ;; list-structure with respect to containing lists.
93 ;; `foo' has a path of (0 4 1) in `((a b c (d foo) f) g)'
94 (path ())
95 ;; set non-nil when somebody works out the indentation to use
96 calculated
97 (last-point indent-point)
98 ;; the position of the open-paren of the innermost containing list
99 (containing-form-start (elt state 1))
100 ;; the column of the above
101 sexp-column)
102 ;; Move to start of innermost containing list
103 (goto-char containing-form-start)
104 (setq sexp-column (current-column))
105 ;; Look over successively less-deep containing forms
106 (while (and (not calculated)
107 (< depth lisp-indent-maximum-backtracking))
108 (let ((containing-sexp (point)))
109 (forward-char 1)
110 (parse-partial-sexp (point) indent-point 1 t)
111 ;; Move to the car of the relevant containing form
112 (let (tem function method)
113 (if (not (looking-at "\\sw\\|\\s_"))
114 ;; This form doesn't seem to start with a symbol
115 (setq function nil method nil)
116 (setq tem (point))
117 (forward-sexp 1)
118 (setq function (downcase (buffer-substring-no-properties
119 tem (point))))
120 (goto-char tem)
121 (setq tem (intern-soft function)
122 method (get tem 'common-lisp-indent-function))
123 (cond ((and (null method)
124 (string-match ":[^:]+" function))
125 ;; The pleblisp package feature
126 (setq function (substring function
127 (1+ (match-beginning 0)))
128 method (get (intern-soft function)
129 'common-lisp-indent-function)))
130 ((and (null method))
131 ;; backwards compatibility
132 (setq method (get tem 'lisp-indent-function)))))
133 (let ((n 0))
134 ;; How far into the containing form is the current form?
135 (if (< (point) indent-point)
136 (while (condition-case ()
137 (progn
138 (forward-sexp 1)
139 (if (>= (point) indent-point)
141 (parse-partial-sexp (point)
142 indent-point 1 t)
143 (setq n (1+ n))
145 (error nil))))
146 (setq path (cons n path)))
148 ;; backwards compatibility.
149 (cond ((null function))
150 ((null method)
151 (when (null (cdr path))
152 ;; (package prefix was stripped off above)
153 (setq method (cond ((string-match "\\`def"
154 function)
155 lisp-indent-defun-method)
156 ((string-match "\\`\\(with\\|do\\)-"
157 function)
158 '(&lambda &body))))))
159 ;; backwards compatibility. Bletch.
160 ((eq method 'defun)
161 (setq method lisp-indent-defun-method)))
163 (cond ((and (memq (char-after (1- containing-sexp)) '(?\' ?\`))
164 (not (eq (char-after (- containing-sexp 2)) ?\#)))
165 ;; No indentation for "'(...)" elements
166 (setq calculated (1+ sexp-column)))
167 ((or (eq (char-after (1- containing-sexp)) ?\,)
168 (and (eq (char-after (1- containing-sexp)) ?\@)
169 (eq (char-after (- containing-sexp 2)) ?\,)))
170 ;; ",(...)" or ",@(...)"
171 (setq calculated normal-indent))
172 ((eq (char-after (1- containing-sexp)) ?\#)
173 ;; "#(...)"
174 (setq calculated (1+ sexp-column)))
175 ((null method))
176 ((integerp method)
177 ;; convenient top-level hack.
178 ;; (also compatible with lisp-indent-function)
179 ;; The number specifies how many `distinguished'
180 ;; forms there are before the body starts
181 ;; Equivalent to (4 4 ... &body)
182 (setq calculated (cond ((cdr path)
183 normal-indent)
184 ((<= (car path) method)
185 ;; `distinguished' form
186 (list (+ sexp-column 4)
187 containing-form-start))
188 ((= (car path) (1+ method))
189 ;; first body form.
190 (+ sexp-column lisp-body-indent))
192 ;; other body form
193 normal-indent))))
194 ((symbolp method)
195 (let ((lisp-indent-error-function function))
196 (setq calculated (funcall method
197 path state indent-point
198 sexp-column normal-indent))))
200 (let ((lisp-indent-error-function function))
201 (setq calculated (lisp-indent-259
202 method path state indent-point
203 sexp-column normal-indent))))))
204 (goto-char containing-sexp)
205 (setq last-point containing-sexp)
206 (unless calculated
207 (condition-case ()
208 (progn (backward-up-list 1)
209 (setq depth (1+ depth)))
210 (error (setq depth lisp-indent-maximum-backtracking))))))
211 calculated)))
214 (defun lisp-indent-report-bad-format (m)
215 (error "%s has a badly-formed %s property: %s"
216 ;; Love those free variable references!!
217 lisp-indent-error-function 'common-lisp-indent-function m))
219 ;; Blame the crufty control structure on dynamic scoping
220 ;; -- not on me!
221 (defun lisp-indent-259 (method path state indent-point
222 sexp-column normal-indent)
223 (catch 'exit
224 (let ((p path)
225 (containing-form-start (elt state 1))
226 n tem tail)
227 ;; Isn't tail-recursion wonderful?
228 (while p
229 ;; This while loop is for destructuring.
230 ;; p is set to (cdr p) each iteration.
231 (if (not (consp method)) (lisp-indent-report-bad-format method))
232 (setq n (1- (car p))
233 p (cdr p)
234 tail nil)
235 (while n
236 ;; This while loop is for advancing along a method
237 ;; until the relevant (possibly &rest/&body) pattern
238 ;; is reached.
239 ;; n is set to (1- n) and method to (cdr method)
240 ;; each iteration.
241 (setq tem (car method))
243 (or (eq tem 'nil) ;default indentation
244 (eq tem '&lambda) ;lambda list
245 (and (eq tem '&body) (null (cdr method)))
246 (and (eq tem '&rest)
247 (consp (cdr method))
248 (null (cddr method)))
249 (integerp tem) ;explicit indentation specified
250 (and (consp tem) ;destructuring
251 (eq (car tem) '&whole)
252 (or (symbolp (cadr tem))
253 (integerp (cadr tem))))
254 (and (symbolp tem) ;a function to call to do the work.
255 (null (cdr method)))
256 (lisp-indent-report-bad-format method))
258 (cond ((and tail (not (consp tem)))
259 ;; indent tail of &rest in same way as first elt of rest
260 (throw 'exit normal-indent))
261 ((eq tem '&body)
262 ;; &body means (&rest <lisp-body-indent>)
263 (throw 'exit
264 (if (and (= n 0) ;first body form
265 (null p)) ;not in subforms
266 (+ sexp-column
267 lisp-body-indent)
268 normal-indent)))
269 ((eq tem '&rest)
270 ;; this pattern holds for all remaining forms
271 (setq tail (> n 0)
273 method (cdr method)))
274 ((> n 0)
275 ;; try next element of pattern
276 (setq n (1- n)
277 method (cdr method))
278 (if (< n 0)
279 ;; Too few elements in pattern.
280 (throw 'exit normal-indent)))
281 ((eq tem 'nil)
282 (throw 'exit (list normal-indent containing-form-start)))
283 ((eq tem '&lambda)
284 (throw 'exit
285 (cond ((null p)
286 (list (+ sexp-column 4) containing-form-start))
287 ((null (cdr p))
288 (+ sexp-column 1))
289 (t normal-indent))))
290 ((integerp tem)
291 (throw 'exit
292 (if (null p) ;not in subforms
293 (list (+ sexp-column tem) containing-form-start)
294 normal-indent)))
295 ((symbolp tem) ;a function to call
296 (throw 'exit
297 (funcall tem path state indent-point
298 sexp-column normal-indent)))
300 ;; must be a destructing frob
301 (if (not (null p))
302 ;; descend
303 (setq method (cddr tem)
304 n nil)
305 (setq tem (cadr tem))
306 (throw 'exit
307 (cond (tail
308 normal-indent)
309 ((eq tem 'nil)
310 (list normal-indent
311 containing-form-start))
312 ((integerp tem)
313 (list (+ sexp-column tem)
314 containing-form-start))
316 (funcall tem path state indent-point
317 sexp-column normal-indent))))))))))))
319 (defun lisp-indent-tagbody (path state indent-point sexp-column normal-indent)
320 (if (not (null (cdr path)))
321 normal-indent
322 (save-excursion
323 (goto-char indent-point)
324 (beginning-of-line)
325 (skip-chars-forward " \t")
326 (list (cond ((looking-at "\\sw\\|\\s_")
327 ;; a tagbody tag
328 (+ sexp-column lisp-tag-indentation))
329 ((integerp lisp-tag-body-indentation)
330 (+ sexp-column lisp-tag-body-indentation))
331 ((eq lisp-tag-body-indentation 't)
332 (condition-case ()
333 (progn (backward-sexp 1) (current-column))
334 (error (1+ sexp-column))))
335 (t (+ sexp-column lisp-body-indent)))
336 ; (cond ((integerp lisp-tag-body-indentation)
337 ; (+ sexp-column lisp-tag-body-indentation))
338 ; ((eq lisp-tag-body-indentation 't)
339 ; normal-indent)
340 ; (t
341 ; (+ sexp-column lisp-body-indent)))
342 (elt state 1)
343 ))))
345 (defun lisp-indent-do (path state indent-point sexp-column normal-indent)
346 (if (>= (car path) 3)
347 (let ((lisp-tag-body-indentation lisp-body-indent))
348 (funcall (function lisp-indent-tagbody)
349 path state indent-point sexp-column normal-indent))
350 (funcall (function lisp-indent-259)
351 '((&whole nil &rest
352 ;; the following causes weird indentation
353 ;;(&whole 1 1 2 nil)
355 (&whole nil &rest 1))
356 path state indent-point sexp-column normal-indent)))
358 (defun lisp-indent-defmethod (path state indent-point sexp-column
359 normal-indent)
360 "Indentation function defmethod."
361 (lisp-indent-259 (if (and (>= (first path) 3)
362 (null (rest path))
363 (save-excursion (goto-char (elt state 1))
364 (forward-char 1)
365 (forward-sexp 3)
366 (backward-sexp)
367 (looking-at ":")))
368 '(4 4 (&whole 4 &rest 4) &body)
369 (get 'defun 'common-lisp-indent-function))
370 path state indent-point sexp-column normal-indent))
373 (defun lisp-indent-function-lambda-hack (path state indent-point
374 sexp-column normal-indent)
375 ;; indent (function (lambda () <newline> <body-forms>)) kludgily.
376 (if (or (cdr path) ; wtf?
377 (> (car path) 3))
378 ;; line up under previous body form
379 normal-indent
380 ;; line up under function rather than under lambda in order to
381 ;; conserve horizontal space. (Which is what #' is for.)
382 (condition-case ()
383 (save-excursion
384 (backward-up-list 2)
385 (forward-char 1)
386 (if (looking-at "\\(lisp:+\\)?function\\(\\Sw\\|\\S_\\)")
387 (+ lisp-body-indent -1 (current-column))
388 (+ sexp-column lisp-body-indent)))
389 (error (+ sexp-column lisp-body-indent)))))
392 (let ((l '((block 1)
393 (case (4 &rest (&whole 2 &rest 1)))
394 (ccase . case) (ecase . case)
395 (typecase . case) (etypecase . case) (ctypecase . case)
396 (catch 1)
397 (cond (&rest (&whole 2 &rest 1)))
398 (defvar (4 2 2))
399 (defclass (6 4 (&whole 2 &rest 1) (&whole 2 &rest 1)))
400 (defconstant . defvar)
401 (defcustom (4 2 2 2))
402 (defparameter . defvar)
403 (defconst . defcustom)
404 (define-condition . defclass)
405 (define-modify-macro (4 &lambda &body))
406 (defsetf (4 &lambda 4 &body))
407 (defun (4 &lambda &body))
408 (define-setf-method . defun)
409 (define-setf-expander . defun)
410 (defmacro . defun) (defsubst . defun) (deftype . defun)
411 (defmethod lisp-indent-defmethod)
412 (defpackage (4 2))
413 (defstruct ((&whole 4 &rest (&whole 2 &rest 1))
414 &rest (&whole 2 &rest 1)))
415 (destructuring-bind
416 ((&whole 6 &rest 1) 4 &body))
417 (do lisp-indent-do)
418 (do* . do)
419 (dolist ((&whole 4 2 1) &body))
420 (dotimes . dolist)
421 (eval-when 1)
422 (flet ((&whole 4 &rest (&whole 1 &lambda &body)) &body))
423 (labels . flet)
424 (macrolet . flet)
425 (generic-flet . flet) (generic-labels . flet)
426 (handler-case (4 &rest (&whole 2 &lambda &body)))
427 (restart-case . handler-case)
428 ;; `else-body' style
429 (if (nil nil &body))
430 ;; single-else style (then and else equally indented)
431 (if (&rest nil))
432 (lambda (&lambda &rest lisp-indent-function-lambda-hack))
433 (let ((&whole 4 &rest (&whole 1 1 2)) &body))
434 (let* . let)
435 (compiler-let . let) ;barf
436 (handler-bind . let) (restart-bind . let)
437 (locally 1)
438 ;(loop ...)
439 (:method (&lambda &body)) ; in `defgeneric'
440 (multiple-value-bind ((&whole 6 &rest 1) 4 &body))
441 (multiple-value-call (4 &body))
442 (multiple-value-prog1 1)
443 (multiple-value-setq (4 2))
444 (multiple-value-setf . multiple-value-setq)
445 (pprint-logical-block (4 2))
446 (print-unreadable-object ((&whole 4 1 &rest 1) &body))
447 ;; Combines the worst features of BLOCK, LET and TAGBODY
448 (prog (&lambda &rest lisp-indent-tagbody))
449 (prog* . prog)
450 (prog1 1)
451 (prog2 2)
452 (progn 0)
453 (progv (4 4 &body))
454 (return 0)
455 (return-from (nil &body))
456 (symbol-macrolet . multiple-value-bind)
457 (tagbody lisp-indent-tagbody)
458 (throw 1)
459 (unless 1)
460 (unwind-protect (5 &body))
461 (when 1)
462 (with-accessors . multiple-value-bind)
463 (with-condition-restarts . multiple-value-bind)
464 (with-output-to-string (4 2))
465 (with-slots . multiple-value-bind)
466 (with-standard-io-syntax (2)))))
467 (dolist (el l)
468 (put (car el) 'common-lisp-indent-function
469 (if (symbolp (cdr el))
470 (get (cdr el) 'common-lisp-indent-function)
471 (car (cdr el))))))
474 ;(defun foo (x)
475 ; (tagbody
476 ; foo
477 ; (bar)
478 ; baz
479 ; (when (losing)
480 ; (with-big-loser
481 ; (yow)
482 ; ((lambda ()
483 ; foo)
484 ; big)))
485 ; (flet ((foo (bar baz zap)
486 ; (zip))
487 ; (zot ()
488 ; quux))
489 ; (do ()
490 ; ((lose)
491 ; (foo 1))
492 ; (quux)
493 ; foo
494 ; (lose))
495 ; (cond ((x)
496 ; (win 1 2
497 ; (foo)))
498 ; (t
499 ; (lose
500 ; 3))))))
503 ;(put 'while 'common-lisp-indent-function 1)
504 ;(put 'defwrapper'common-lisp-indent-function ...)
505 ;(put 'def 'common-lisp-indent-function ...)
506 ;(put 'defflavor 'common-lisp-indent-function ...)
507 ;(put 'defsubst 'common-lisp-indent-function ...)
509 ;(put 'with-restart 'common-lisp-indent-function '((1 4 ((* 1))) (2 &body)))
510 ;(put 'restart-case 'common-lisp-indent-function '((1 4) (* 2 ((0 1) (* 1)))))
511 ;(put 'define-condition 'common-lisp-indent-function '((1 6) (2 6 ((&whole 1))) (3 4 ((&whole 1))) (4 &body)))
512 ;(put 'with-condition-handler 'common-lisp-indent-function '((1 4 ((* 1))) (2 &body)))
513 ;(put 'condition-case 'common-lisp-indent-function '((1 4) (* 2 ((0 1) (1 3) (2 &body)))))
514 ;(put 'defclass 'common-lisp-indent-function '((&whole 2 &rest (&whole 2 &rest 1) &rest (&whole 2 &rest 1)))
515 ;(put 'defgeneric 'common-lisp-indent-function 'defun)
517 ;;; cl-indent.el ends here