Common Lisp indentation improvements on defmethod and lambda-lists.

* cl-indent.el: Advertise the changes and remove obsolete TODO entries.
(lisp-lambda-list-keyword-parameter-indentation)
(lisp-lambda-list-keyword-parameter-alignment)
(lisp-lambda-list-keyword-alignment): New customizable user options.
(lisp-indent-defun-method): Improve docstring.
(extended-loop-p): Fix comment.
(lisp-indent-lambda-list-keywords-regexp): New variable.
(lisp-indent-lambda-list): New function.
(lisp-indent-259): Use it.
(lisp-indent-defmethod): Support for more than one
method qualifier and properly indent methods lambda-lists.
(defgeneric): Provide a missing common-lisp-indent-function property.
This commit is contained in:
Didier Verna 2011-05-13 16:27:52 -03:00 committed by Stefan Monnier
parent f278f87fe6
commit 8497a2977c
2 changed files with 162 additions and 35 deletions

View file

@ -31,22 +31,10 @@
;;
;; (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)
@ -101,9 +89,55 @@ If nil, indent backquoted lists as data, i.e., like quoted lists."
: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)
(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)
@ -144,7 +178,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:
@ -217,8 +251,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
@ -381,10 +414,74 @@ For example, the function `case' has an indent property
;; 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))
@ -452,8 +549,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
@ -523,19 +626,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
@ -577,6 +687,7 @@ 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)