Add 2012 to FSF copyright years for Emacs files
[bpt/emacs.git] / lisp / emacs-lisp / cl-indent.el
index 9efa43e..0a690af 100644 (file)
@@ -1,12 +1,12 @@
 ;;; cl-indent.el --- enhanced lisp-indent mode
 
-;; Copyright (C) 1987, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-;;   2008, 2009 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
@@ -78,33 +66,78 @@ by `lisp-body-indent'."
   :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)
-  "Indentation for function with `common-lisp-indent-function' property `defun'.")
+  "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)
@@ -126,14 +159,19 @@ If nil, indent backquoted lists as data, i.e., like quoted lists."
                             (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.
@@ -145,7 +183,7 @@ indentation function is called, and STATE is the
 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
+function's `common-lisp-indent-function' property specifies how
 this function should indent it.  Possible values for this
 property are:
 
@@ -218,8 +256,7 @@ For example, the function `case' has an indent property
     (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)'
-          ;; (Surely (0 3 1)?).
+          ;; `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
@@ -374,15 +411,82 @@ For example, the function `case' has an indent property
       (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))
@@ -450,8 +554,14 @@ For example, the function `case' has an indent property
                        (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
@@ -521,19 +631,26 @@ For example, the function `case' has an indent property
              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
@@ -558,8 +675,11 @@ For example, the function `case' has an indent property
 \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))
@@ -572,9 +692,12 @@ For example, the function `case' has an indent property
            (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))
@@ -589,7 +712,8 @@ For example, the function `case' has an indent property
            (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
@@ -600,7 +724,8 @@ For example, the function `case' has an indent property
            (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'
@@ -681,5 +806,4 @@ For example, the function `case' has an indent property
 ;(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