Subject: Re: bug#1012: Indentation of constant symbols

From: Markus Sauermann <[email-masked]>
Date: Wed, 01 Oct 2008 22:21:11 +0200

Hello,

my bug fix was not farsighted enough. Basically I fixed a part of the
source code, which does nothing at all.
I dug a bit deeper and noticed, that the following "source code 1" of
the function calculate-lisp-indent in lisp-mode.el is a little bugged.

--- BEGIN source code 1 ---
                ;; If the function has no special alignment
		;; or it does not apply to this argument,
		;; try to align a constant-symbol under the last
                ;; preceding constant symbol, if there is such one of
                ;; the last 2 preceding symbols, in the previous
                ;; uncommented line.
                (and (save-excursion
                       (goto-char indent-point)
                       (skip-chars-forward " \t")
                       (looking-at ":"))
                     ;; The last sexp may not be at the indentation
                     ;; where it begins, so find that one, instead.
                     (save-excursion
                       (goto-char calculate-lisp-indent-last-sexp)
		       ;; Handle prefix characters and whitespace
		       ;; following an open paren.  (Bug#1012)
                       (backward-prefix-chars)
                       (while (and (not (looking-back "^[ \t]*\\|([ \t]+"))
                                   (or (not containing-sexp)
                                       (< (1+ containing-sexp) (point))))
                         (forward-sexp -1)
                         (backward-prefix-chars))
                       (setq calculate-lisp-indent-last-sexp (point)))
                     (> calculate-lisp-indent-last-sexp
                        (save-excursion
                          (goto-char (1+ containing-sexp))
                          (parse-partial-sexp (point)
calculate-lisp-indent-last-sexp 0 t)
                          (point)))
                     (let ((parse-sexp-ignore-comments t)
                           indent)
                       (goto-char calculate-lisp-indent-last-sexp)
                       (or (and (looking-at ":")
                                (setq indent (current-column)))
                           (and (< (save-excursion (beginning-of-line)
(point))
                                   (prog2 (backward-sexp) (point)))
                                (looking-at ":")
                                (setq indent (current-column))))
                       indent))
--- END source code 1 ---

One part of that code implements the functionality as described in the
comment at the beginning of the code. The other part tries to fix a
problem, that is caused by this functionality, which is denoted in
example 1, where :baz should be aligned under :foo and not under :foo2.

--- BEGIN example 1 ---
(:foo bar :foo2 bar2
          :baz ar)
--- END example 1 ---

But this other part makes constant symbols to be indented in the same
way as the usual indentation does. So discarding the whole code does not
change any indentation except in some rare, mostly unused cases. See
examples 2 for code snippets that are indented in the same way with or
without this code.

--- BEGIN examples 2 ---
(:foo bar
      :baz)

(foo bar :baz
     :foo2)

(foo bar :baz bor
     :foo2)

(:foo bar (:bar baz
                :asd))
--- END examples 2 ---

These examples also contradict the description of the source code. I
even think that the description of the code is not what the special
handling of constant symbols should be like, as it causes the
indentation described in example 1.

So I suggest to treat constant symbols in the following way:

If the sexp, preceding the constant symbol, begins in the same line as
the containing sexp, then align it under the first appearance of a
constant symbol between the beginning of the containing sexp and
the the begin of the preceding sexp.

If the sexp, preceding the constant symbol, does not begin in the same
line as the containing sexp, then special treatment is not necessary,
because it is handled by the usual indentation.

Do not treat a constant symbol in a special way, if the containing sexp
is a vector, since this would cause indentations in unwanted places.

After applying "patch-1.txt", which implements this suggestions, the
code in examples 2 is indented as shown in examples 3.

--- BEGIN examples 3 ---
(:foo bar
 :baz)

(foo bar :baz
         :foo2)

(foo bar :baz bor
         :foo2)

(:foo bar (:bar baz
           :asd))
--- END examples 3 ---

This implementation still has shortcomings in several unusual
conditions, as examples 4 show, but I think this patch covers all common
usages.

--- BEGIN examples 4 ---
(:foo cons :foo2
      (bar baz :asd asd)
      :sad)

(:foo (:bar baz
       :asd asd) :con con2
       :sad)
--- END examples 4 ---

Regards,
Markus Sauermann
Index: lisp-mode.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/emacs-lisp/lisp-mode.el,v
retrieving revision 1.228
diff -c -r1.228 lisp-mode.el
*** lisp-mode.el	23 Sep 2008 17:59:28 -0000	1.228
--- lisp-mode.el	1 Oct 2008 19:10:51 -0000
***************
*** 1014,1055 ****
                       (not retry)
                       (funcall lisp-indent-function indent-point state))
                  ;; If the function has no special alignment
! 		;; or it does not apply to this argument,
! 		;; try to align a constant-symbol under the last
!                 ;; preceding constant symbol, if there is such one of
!                 ;; the last 2 preceding symbols, in the previous
!                 ;; uncommented line.
                  (and (save-excursion
                         (goto-char indent-point)
                         (skip-chars-forward " \t")
                         (looking-at ":"))
!                      ;; The last sexp may not be at the indentation
!                      ;; where it begins, so find that one, instead.
!                      (save-excursion
!                        (goto-char calculate-lisp-indent-last-sexp)
! 		       ;; Handle prefix characters and whitespace
! 		       ;; following an open paren.  (Bug#1012)
!                        (backward-prefix-chars)
!                        (while (and (not (looking-back "^[ \t]*\\|([ \t]+"))
!                                    (or (not containing-sexp)
!                                        (< (1+ containing-sexp) (point))))
!                          (forward-sexp -1)
!                          (backward-prefix-chars))
!                        (setq calculate-lisp-indent-last-sexp (point)))
!                      (> calculate-lisp-indent-last-sexp
!                         (save-excursion
!                           (goto-char (1+ containing-sexp))
!                           (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
!                           (point)))
                       (let ((parse-sexp-ignore-comments t)
                             indent)
!                        (goto-char calculate-lisp-indent-last-sexp)
!                        (or (and (looking-at ":")
!                                 (setq indent (current-column)))
!                            (and (< (save-excursion (beginning-of-line) (point))
!                                    (prog2 (backward-sexp) (point)))
!                                 (looking-at ":")
!                                 (setq indent (current-column))))
                         indent))
                  ;; another symbols or constants not preceded by a constant
                  ;; as defined above.
--- 1014,1046 ----
                       (not retry)
                       (funcall lisp-indent-function indent-point state))
                  ;; If the function has no special alignment
!                 ;; or it does not apply to this argument,
!                 ;; try to treat a constant symbol in a special way.
                  (and (save-excursion
                         (goto-char indent-point)
                         (skip-chars-forward " \t")
                         (looking-at ":"))
!                      ;; Treat it only special, if the sexp preceding it begins
!                      ;; in the same line as the containing sexp
!                      (< calculate-lisp-indent-last-sexp
!                         (save-excursion (goto-char containing-sexp)
!                                         (end-of-line) (point)))
!                      ;; Don't treat it special, if the containing sexp is a
!                      ;; vector
!                      (save-excursion (goto-char containing-sexp)
!                                      (not (looking-at "\\[")))
!                      ;; Align it under the first constant symbol, if any,
!                      ;; between the beginning of the containing sexp and
!                      ;; the preceding sexp
                       (let ((parse-sexp-ignore-comments t)
                             indent)
!                        (goto-char (1+ containing-sexp))
!                        (while (and (<= (point) calculate-lisp-indent-last-sexp)
!                                    (not indent))
!                          (skip-syntax-forward "-")
!                          (if (looking-at ":")
!                              (setq indent (current-column))
!                            (forward-sexp)))
                         indent))
                  ;; another symbols or constants not preceded by a constant
                  ;; as defined above.