;;; cl-indent.el --- enhanced lisp-indent mode
-;; Copyright (C) 1987, 2000, 2001, 2002, 2003, 2004,
-;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 1987, 2000-2012 Free Software Foundation, Inc.
;; Author: Richard Mlynarik <mly@eddie.mit.edu>
;; Created: July 1987
;; Maintainer: FSF
;; Keywords: lisp, tools
+;; Package: emacs
;; This file is part of GNU Emacs.
;;
;; (setq lisp-indent-function 'common-lisp-indent-function)
-;;>> TODO
-;; :foo
-;; bar
-;; :baz
-;; zap
-;; &key (like &body)??
-
-;; &rest 1 in lambda-lists doesn't work
-;; -- really want (foo bar
-;; baz)
-;; not (foo bar
-;; baz)
-;; Need something better than &rest for such cases
-
;;; Code:
+(eval-when-compile (require 'cl))
+
(defgroup lisp-indent nil
"Indentation in Lisp."
:group 'lisp)
(defcustom lisp-indent-maximum-backtracking 3
- "*Maximum depth to backtrack out from a sublist for structured indentation.
+ "Maximum depth to backtrack out from a sublist for structured indentation.
If this variable is 0, no backtracking will occur and forms such as `flet'
may not be correctly indented."
:type 'integer
:group 'lisp-indent)
(defcustom lisp-tag-indentation 1
- "*Indentation of tags relative to containing list.
+ "Indentation of tags relative to containing list.
This variable is used by the function `lisp-indent-tagbody'."
:type 'integer
:group 'lisp-indent)
(defcustom lisp-tag-body-indentation 3
- "*Indentation of non-tagged lines relative to containing list.
+ "Indentation of non-tagged lines relative to containing list.
This variable is used by the function `lisp-indent-tagbody' to indent normal
lines (lines without tags).
The indentation is relative to the indentation of the parenthesis enclosing
:group 'lisp-indent)
(defcustom lisp-backquote-indentation t
- "*Whether or not to indent backquoted lists as code.
+ "Whether or not to indent backquoted lists as code.
If nil, indent backquoted lists as data, i.e., like quoted lists."
:type 'boolean
:group 'lisp-indent)
(defcustom lisp-loop-keyword-indentation 3
- "*Indentation of loop keywords in extended loop forms."
+ "Indentation of loop keywords in extended loop forms."
:type 'integer
:group 'lisp-indent)
(defcustom lisp-loop-forms-indentation 5
- "*Indentation of forms in extended loop forms."
+ "Indentation of forms in extended loop forms."
:type 'integer
:group 'lisp-indent)
(defcustom lisp-simple-loop-indentation 3
- "*Indentation of forms in simple loop forms."
+ "Indentation of forms in simple loop forms."
+ :type 'integer
+ :group 'lisp-indent)
+
+(defcustom lisp-lambda-list-keyword-alignment nil
+ "Whether to vertically align lambda-list keywords together.
+If nil (the default), keyworded lambda-list parts are aligned
+with the initial mandatory arguments, like this:
+
+\(defun foo (arg1 arg2 &rest rest
+ &key key1 key2)
+ #|...|#)
+
+If non-nil, alignment is done with the first keyword
+\(or falls back to the previous case), as in:
+
+\(defun foo (arg1 arg2 &rest rest
+ &key key1 key2)
+ #|...|#)"
+ :type 'boolean
+ :group 'lisp-indent)
+
+(defcustom lisp-lambda-list-keyword-parameter-indentation 2
+ "Indentation of lambda list keyword parameters.
+See `lisp-lambda-list-keyword-parameter-alignment'
+for more information."
:type 'integer
:group 'lisp-indent)
+(defcustom lisp-lambda-list-keyword-parameter-alignment nil
+ "Whether to vertically align lambda-list keyword parameters together.
+If nil (the default), the parameters are aligned
+with their corresponding keyword, plus the value of
+`lisp-lambda-list-keyword-parameter-indentation', like this:
+
+\(defun foo (arg1 arg2 &key key1 key2
+ key3 key4)
+ #|...|#)
+
+If non-nil, alignment is done with the first parameter
+\(or falls back to the previous case), as in:
+
+\(defun foo (arg1 arg2 &key key1 key2
+ key3 key4)
+ #|...|#)"
+ :type 'boolean
+ :group 'lisp-indent)
+
\f
-(defvar lisp-indent-error-function)
-(defvar lisp-indent-defun-method '(4 &lambda &body))
+(defvar lisp-indent-defun-method '(4 &lambda &body)
+ "Defun-like indentation method.
+This applies when the value of the `common-lisp-indent-function' property
+is set to `defun'.")
(defun extended-loop-p (loop-start)
(current-column))))
(goto-char indent-point)
(beginning-of-line)
- (cond ((not (extended-loop-p (elt state 1)))
- (+ loop-indentation lisp-simple-loop-indentation))
- ((looking-at "^\\s-*\\(:?\\sw+\\|;\\)")
- (+ loop-indentation lisp-loop-keyword-indentation))
- (t
- (+ loop-indentation lisp-loop-forms-indentation)))))
-
-
+ (list
+ (cond ((not (extended-loop-p (elt state 1)))
+ (+ loop-indentation lisp-simple-loop-indentation))
+ ((looking-at "^\\s-*\\(:?\\sw+\\|;\\)")
+ (+ loop-indentation lisp-loop-keyword-indentation))
+ (t
+ (+ loop-indentation lisp-loop-forms-indentation)))
+ ;; Tell the caller that the next line needs recomputation, even
+ ;; though it doesn't start a sexp.
+ loop-indentation)))
+
+
+;; Cf (info "(elisp)Specification List")
;;;###autoload
(defun common-lisp-indent-function (indent-point state)
+ "Function to indent the arguments of a Lisp function call.
+This is suitable for use as the value of the variable
+`lisp-indent-function'. INDENT-POINT is the point at which the
+indentation function is called, and STATE is the
+`parse-partial-sexp' state at that position. Browse the
+`lisp-indent' customize group for options affecting the behavior
+of this function.
+
+If the indentation point is in a call to a Lisp function, that
+function's `common-lisp-indent-function' property specifies how
+this function should indent it. Possible values for this
+property are:
+
+* defun, meaning indent according to `lisp-indent-defun-method';
+ i.e., like (4 &lambda &body), as explained below.
+
+* any other symbol, meaning a function to call. The function should
+ take the arguments: PATH STATE INDENT-POINT SEXP-COLUMN NORMAL-INDENT.
+ PATH is a list of integers describing the position of point in terms of
+ list-structure with respect to the containing lists. For example, in
+ ((a b c (d foo) f) g), foo has a path of (0 3 1). In other words,
+ to reach foo take the 0th element of the outermost list, then
+ the 3rd element of the next list, and finally the 1st element.
+ STATE and INDENT-POINT are as in the arguments to
+ `common-lisp-indent-function'. SEXP-COLUMN is the column of
+ the open parenthesis of the innermost containing list.
+ NORMAL-INDENT is the column the indentation point was
+ originally in. This function should behave like `lisp-indent-259'.
+
+* an integer N, meaning indent the first N arguments like
+ function arguments, and any further arguments like a body.
+ This is equivalent to (4 4 ... &body).
+
+* a list. The list element in position M specifies how to indent the Mth
+ function argument. If there are fewer elements than function arguments,
+ the last list element applies to all remaining arguments. The accepted
+ list elements are:
+
+ * nil, meaning the default indentation.
+
+ * an integer, specifying an explicit indentation.
+
+ * &lambda. Indent the argument (which may be a list) by 4.
+
+ * &rest. When used, this must be the penultimate element. The
+ element after this one applies to all remaining arguments.
+
+ * &body. This is equivalent to &rest lisp-body-indent, i.e., indent
+ all remaining elements by `lisp-body-indent'.
+
+ * &whole. This must be followed by nil, an integer, or a
+ function symbol. This indentation is applied to the
+ associated argument, and as a base indent for all remaining
+ arguments. For example, an integer P means indent this
+ argument by P, and all remaining arguments by P, plus the
+ value specified by their associated list element.
+
+ * a symbol. A function to call, with the 6 arguments specified above.
+
+ * a list, with elements as described above. This applies when the
+ associated function argument is itself a list. Each element of the list
+ specifies how to indent the associated argument.
+
+For example, the function `case' has an indent property
+\(4 &rest (&whole 2 &rest 1)), meaning:
+ * indent the first argument by 4.
+ * arguments after the first should be lists, and there may be any number
+ of them. The first list element has an offset of 2, all the rest
+ have an offset of 2+1=3."
(if (save-excursion (goto-char (elt state 1))
(looking-at "([Ll][Oo][Oo][Pp]"))
(common-lisp-loop-part-indentation indent-point state)
(let ((depth 0)
;; Path describes the position of point in terms of
;; list-structure with respect to containing lists.
- ;; `foo' has a path of (0 4 1) in `((a b c (d foo) f) g)'
+ ;; `foo' has a path of (0 3 1) in `((a b c (d foo) f) g)'.
(path ())
;; set non-nil when somebody works out the indentation to use
calculated
(lisp-indent-259 method path state indent-point
sexp-column normal-indent))))
+;; Dynamically bound in common-lisp-indent-call-method.
+(defvar lisp-indent-error-function)
+
(defun lisp-indent-report-bad-format (m)
(error "%s has a badly-formed %s property: %s"
;; Love those free variable references!!
lisp-indent-error-function 'common-lisp-indent-function m))
+
+;; Lambda-list indentation is now done in LISP-INDENT-LAMBDA-LIST.
+;; See also `lisp-lambda-list-keyword-alignment',
+;; `lisp-lambda-list-keyword-parameter-alignment' and
+;; `lisp-lambda-list-keyword-parameter-indentation' -- dvl
+
+(defvar lisp-indent-lambda-list-keywords-regexp
+ "&\\(\
+optional\\|rest\\|key\\|allow-other-keys\\|aux\\|whole\\|body\\|environment\
+\\)\\([ \t]\\|$\\)"
+ "Regular expression matching lambda-list keywords.")
+
+(defun lisp-indent-lambda-list
+ (indent-point sexp-column containing-form-start)
+ (let (limit)
+ (cond ((save-excursion
+ (goto-char indent-point)
+ (beginning-of-line)
+ (skip-chars-forward " \t")
+ (setq limit (point))
+ (looking-at lisp-indent-lambda-list-keywords-regexp))
+ ;; We're facing a lambda-list keyword.
+ (if lisp-lambda-list-keyword-alignment
+ ;; Align to the first keyword if any, or to the beginning of
+ ;; the lambda-list.
+ (save-excursion
+ (goto-char containing-form-start)
+ (save-match-data
+ (if (re-search-forward
+ lisp-indent-lambda-list-keywords-regexp
+ limit t)
+ (progn
+ (goto-char (match-beginning 0))
+ (current-column))
+ (1+ sexp-column))))
+ ;; Align to the beginning of the lambda-list.
+ (1+ sexp-column)))
+ (t
+ ;; Otherwise, align to the first argument of the last lambda-list
+ ;; keyword, the keyword itself, or the beginning of the
+ ;; lambda-list.
+ (save-excursion
+ (goto-char indent-point)
+ (forward-line -1)
+ (end-of-line)
+ (save-match-data
+ (if (re-search-backward lisp-indent-lambda-list-keywords-regexp
+ containing-form-start t)
+ (let* ((keyword-posn
+ (progn
+ (goto-char (match-beginning 0))
+ (current-column)))
+ (indented-keyword-posn
+ (+ keyword-posn
+ lisp-lambda-list-keyword-parameter-indentation)))
+ (goto-char (match-end 0))
+ (skip-chars-forward " \t")
+ (if (eolp)
+ indented-keyword-posn
+ (if lisp-lambda-list-keyword-parameter-alignment
+ (current-column)
+ indented-keyword-posn)))
+ (1+ sexp-column))))))))
+
;; Blame the crufty control structure on dynamic scoping
;; -- not on me!
-(defun lisp-indent-259 (method path state indent-point
- sexp-column normal-indent)
+(defun lisp-indent-259
+ (method path state indent-point sexp-column normal-indent)
(catch 'exit
(let ((p path)
(containing-form-start (elt state 1))
(cond ((null p)
(list (+ sexp-column 4) containing-form-start))
((null (cdr p))
- (+ sexp-column 1))
- (t normal-indent))))
+ ;; Indentation within a lambda-list. -- dvl
+ (list (lisp-indent-lambda-list
+ indent-point
+ sexp-column
+ containing-form-start)
+ containing-form-start))
+ (t
+ normal-indent))))
((integerp tem)
(throw 'exit
(if (null p) ;not in subforms
path state indent-point sexp-column normal-indent)))
-(defun lisp-indent-defmethod (path state indent-point sexp-column
- normal-indent)
- "Indentation function defmethod."
- (lisp-indent-259 (if (and (>= (car path) 3)
- (null (cdr path))
- (save-excursion (goto-char (elt state 1))
- (forward-char 1)
- (forward-sexp 3)
- (backward-sexp)
- (looking-at ":\\|\\sw+")))
- '(4 4 (&whole 4 &rest 4) &body)
- (get 'defun 'common-lisp-indent-function))
- path state indent-point sexp-column normal-indent))
+;; LISP-INDENT-DEFMETHOD now supports the presence of more than one method
+;; qualifier and indents the method's lambda list properly. -- dvl
+(defun lisp-indent-defmethod
+ (path state indent-point sexp-column normal-indent)
+ (lisp-indent-259
+ (let ((nqual 0))
+ (if (and (>= (car path) 3)
+ (save-excursion
+ (beginning-of-defun)
+ (forward-char 1)
+ (forward-sexp 2)
+ (skip-chars-forward " \t\n")
+ (while (looking-at "\\sw\\|\\s_")
+ (incf nqual)
+ (forward-sexp)
+ (skip-chars-forward " \t\n"))
+ (> nqual 0)))
+ (append '(4) (make-list nqual 4) '(&lambda &body))
+ (get 'defun 'common-lisp-indent-function)))
+ path state indent-point sexp-column normal-indent))
(defun lisp-indent-function-lambda-hack (path state indent-point
\f
(let ((l '((block 1)
(case (4 &rest (&whole 2 &rest 1)))
- (ccase . case) (ecase . case)
- (typecase . case) (etypecase . case) (ctypecase . case)
+ (ccase . case)
+ (ecase . case)
+ (typecase . case)
+ (etypecase . case)
+ (ctypecase . case)
(catch 1)
(cond (&rest (&whole 2 &rest 1)))
(defvar (4 2 2))
(define-modify-macro (4 &lambda &body))
(defsetf (4 &lambda 4 &body))
(defun (4 &lambda &body))
+ (defgeneric (4 &lambda &body))
(define-setf-method . defun)
(define-setf-expander . defun)
- (defmacro . defun) (defsubst . defun) (deftype . defun)
+ (defmacro . defun)
+ (defsubst . defun)
+ (deftype . defun)
(defmethod lisp-indent-defmethod)
(defpackage (4 2))
(defstruct ((&whole 4 &rest (&whole 2 &rest 1))
(flet ((&whole 4 &rest (&whole 1 &lambda &body)) &body))
(labels . flet)
(macrolet . flet)
- (generic-flet . flet) (generic-labels . flet)
+ (generic-flet . flet)
+ (generic-labels . flet)
(handler-case (4 &rest (&whole 2 &lambda &body)))
(restart-case . handler-case)
;; `else-body' style
(let ((&whole 4 &rest (&whole 1 1 2)) &body))
(let* . let)
(compiler-let . let) ;barf
- (handler-bind . let) (restart-bind . let)
+ (handler-bind . let)
+ (restart-bind . let)
(locally 1)
;(loop lisp-indent-loop)
(:method (&lambda &body)) ; in `defgeneric'
;(put 'defclass 'common-lisp-indent-function '((&whole 2 &rest (&whole 2 &rest 1) &rest (&whole 2 &rest 1)))
;(put 'defgeneric 'common-lisp-indent-function 'defun)
-;; arch-tag: 7914d50f-92ec-4476-93fc-0f043a380e03
;;; cl-indent.el ends here