Subject: bug#954: indentation in latex-mode in emacs-23

From: martin rudalics <[email-masked]>
Date: Wed, 24 Sep 2008 10:18:19 +0200

> It's not something I can convince you about. And I don't think there
  > is any correct or incorrect formatting style. It's just from my 10
  > years experience. As I get older, it gets harder to accommodate new
  > things no matter how small they are....

... sounds convincing.  What about the attached patch?

martin

*** textmodes/tex-mode.el.~1.218.~	2008-09-23 10:17:14.531250000 +0200
--- textmodes/tex-mode.el	2008-09-24 09:57:17.203125000 +0200
***************
*** 1482,1487 ****
--- 1482,1489 ----
      (push-mark)
      (goto-char spot)))
  
+ (defvar latex-handle-escaped-parens t)
+ 
  ;; Don't think this one actually _needs_ (for the purposes of
  ;; tex-mode) to handle escaped parens.
  (defun latex-backward-sexp-1 ()
***************
*** 1489,1499 ****
    (let ((pos (point))
  	(forward-sexp-function))
      (backward-sexp 1)
!     (cond ((looking-at "\\\\\\(begin\\>\\|[[({]\\)")
  	   (signal 'scan-error
  		   (list "Containing expression ends prematurely"
  			 (point) (prog1 (point) (goto-char pos)))))
! 	  ((looking-at "\\\\\\([])}]\\)")
  	   (tex-last-unended-eparen (match-string 1)))
  	  ((eq (char-after) ?{)
  	   (let ((newpos (point)))
--- 1491,1505 ----
    (let ((pos (point))
  	(forward-sexp-function))
      (backward-sexp 1)
!     (cond ((looking-at
! 	    (if latex-handle-escaped-parens
! 		"\\\\\\(begin\\>\\|[[({]\\)"
! 	      "\\\\begin\\>"))
  	   (signal 'scan-error
  		   (list "Containing expression ends prematurely"
  			 (point) (prog1 (point) (goto-char pos)))))
! 	  ((and latex-handle-escaped-parens
! 		(looking-at "\\\\\\([])}]\\)"))
  	   (tex-last-unended-eparen (match-string 1)))
  	  ((eq (char-after) ?{)
  	   (let ((newpos (point)))
***************
*** 1528,1539 ****
  	(tex-next-unmatched-end))
         ;; A better way to handle this, \( .. \) etc, is probably to
         ;; temporarily change the syntax of the \ in \( to punctuation.
!        ((looking-back "\\\\[])}]")
  	(signal 'scan-error
  		(list "Containing expression ends prematurely"
  		      (- (point) 2) (prog1 (point)
  				      (goto-char pos)))))
!        ((looking-back "\\\\\\([({[]\\)")
  	(tex-next-unmatched-eparen (match-string 1)))
         (t (goto-char newpos))))))
  
--- 1534,1547 ----
  	(tex-next-unmatched-end))
         ;; A better way to handle this, \( .. \) etc, is probably to
         ;; temporarily change the syntax of the \ in \( to punctuation.
!        ((and latex-handle-escaped-parens
! 	     (looking-back "\\\\[])}]"))
  	(signal 'scan-error
  		(list "Containing expression ends prematurely"
  		      (- (point) 2) (prog1 (point)
  				      (goto-char pos)))))
!        ((and latex-handle-escaped-parens
! 	     (looking-back "\\\\\\([({[]\\)"))
  	(tex-next-unmatched-eparen (match-string 1)))
         (t (goto-char newpos))))))
  
***************
*** 2568,2670 ****
  	    (indent-line-to indent)
  	  (save-excursion (indent-line-to indent)))))))
  
  (defun latex-find-indent (&optional virtual)
    "Find the proper indentation of text after point.
  VIRTUAL if non-nil indicates that we're only trying to find the indentation
    in order to determine the indentation of something else.
  There might be text before point."
!   (save-excursion
!     (skip-chars-forward " \t")
!     (or
!      ;; Stick the first line at column 0.
!      (and (= (point-min) (line-beginning-position)) 0)
!      ;; Trust the current indentation, if such info is applicable.
!      (and virtual (save-excursion (skip-chars-backward " \t&") (bolp))
! 	  (current-column))
!      ;; Stick verbatim environments to the left margin.
!      (and (looking-at "\\\\\\(begin\\|end\\) *{\\([^\n}]+\\)")
! 	  (member (match-string 2) tex-verbatim-environments)
! 	  0)
!      ;; Put leading close-paren where the matching open paren would be.
!      (let (escaped)
!        (and (or (eq (latex-syntax-after) ?\))
! 		;; Try to handle escaped close parens but keep original
! 		;; position if it doesn't work out.
! 		(setq escaped (looking-at "\\\\\\([])}]\\)")))
! 	    (ignore-errors
! 	     (save-excursion
! 	       (when escaped
! 		 (goto-char (match-beginning 1)))
! 	       (latex-skip-close-parens)
  	       (latex-backward-sexp-1)
! 	       (latex-find-indent 'virtual)))))
!      ;; Default (maybe an argument)
!      (let ((pos (point))
! 	   ;; Outdent \item if necessary.
! 	   (indent (if (looking-at tex-indent-item-re) (- tex-indent-item) 0))
! 	   up-list-pos)
!        ;; Find the previous point which determines our current indentation.
!        (condition-case err
! 	   (progn
! 	     (latex-backward-sexp-1)
! 	     (while (> (current-column) (current-indentation))
! 	       (latex-backward-sexp-1)))
! 	 (scan-error
! 	  (setq up-list-pos (nth 2 err))))
!        (cond
! 	((= (point-min) pos) 0) ; We're really just indenting the first line.
! 	((integerp up-list-pos)
! 	 ;; Have to indent relative to the open-paren.
! 	 (goto-char up-list-pos)
! 	 (if (and (not tex-indent-allhanging)
! 		  (save-excursion
! 		    ;; Make sure we're an argument to a macro and
! 		    ;; that the macro is at the beginning of a line.
! 		    (condition-case nil
! 			(progn
! 			  (while (eq (char-syntax (char-after)) ?\()
! 			    (forward-sexp -1))
! 			  (and (eq (char-syntax (char-after)) ?/)
! 			       (progn (skip-chars-backward " \t&")
! 				      (bolp))))
! 		      (scan-error nil)))
! 		  (> pos (progn (latex-down-list)
! 				(forward-comment (point-max))
! 				(point))))
! 		 ;; Align with the first element after the open-paren.
! 	     (current-column)
! 	   ;; We're the first element after a hanging brace.
  	   (goto-char up-list-pos)
! 	   (+ (if (and (looking-at "\\\\begin *{\\([^\n}]+\\)")
! 		       (member (match-string 1)
! 			       latex-noindent-environments))
! 		  0 tex-indent-basic)
! 	      indent (latex-find-indent 'virtual))))
! 	;; We're now at the "beginning" of a line.
! 	((not (and (not virtual) (eq (char-after) ?\\)))
! 	 ;; Nothing particular here: just keep the same indentation.
! 	 (+ indent (current-column)))
! 	;; We're now looking at a macro call.
! 	((looking-at tex-indent-item-re)
! 	 ;; Indenting relative to an item, have to re-add the outdenting.
! 	 (+ indent (current-column) tex-indent-item))
! 	(t
! 	 (let ((col (current-column)))
! 	   (if (or (not (eq (char-syntax (or (char-after pos) ?\s)) ?\())
! 		   ;; Can't be an arg if there's an empty line inbetween.
! 		   (save-excursion (re-search-forward "^[ \t]*$" pos t)))
! 	       ;; If the first char was not an open-paren, there's
! 	       ;; a risk that this is really not an argument to the
! 	       ;; macro at all.
! 	       (+ indent col)
! 	     (forward-sexp 1)
! 	     (if (< (line-end-position)
! 		    (save-excursion (forward-comment (point-max))
! 				    (point)))
! 		 ;; we're indenting the first argument.
! 		 (min (current-column) (+ tex-indent-arg col))
! 	       (skip-syntax-forward " ")
! 	       (current-column))))))))))
  ;;; DocTeX support
  
  (defun doctex-font-lock-^^A ()
--- 2576,2690 ----
  	    (indent-line-to indent)
  	  (save-excursion (indent-line-to indent)))))))
  
+ (defcustom latex-indent-within-escaped-parens nil
+   "Non-nil means add extra indent to text within escaped parens.
+ When this is non-nil, text within matching pairs of escaped
+ parens is indented at the column following the open paren.  The
+ default value does not add any extra indent thus providing the
+ behavior of Emacs 22 and earlier."
+   :type 'boolean
+   :group 'tex
+   :version "23.1")
+ 
  (defun latex-find-indent (&optional virtual)
    "Find the proper indentation of text after point.
  VIRTUAL if non-nil indicates that we're only trying to find the indentation
    in order to determine the indentation of something else.
  There might be text before point."
!   (let ((latex-handle-escaped-parens latex-indent-within-escaped-parens))
!     (save-excursion
!       (skip-chars-forward " \t")
!       (or
!        ;; Stick the first line at column 0.
!        (and (= (point-min) (line-beginning-position)) 0)
!        ;; Trust the current indentation, if such info is applicable.
!        (and virtual (save-excursion (skip-chars-backward " \t&") (bolp))
! 	    (current-column))
!        ;; Stick verbatim environments to the left margin.
!        (and (looking-at "\\\\\\(begin\\|end\\) *{\\([^\n}]+\\)")
! 	    (member (match-string 2) tex-verbatim-environments)
! 	    0)
!        ;; Put leading close-paren where the matching open paren would be.
!        (let (escaped)
! 	 (and (or (eq (latex-syntax-after) ?\))
! 		  ;; Try to handle escaped close parens but keep
! 		  ;; original position if it doesn't work out.
! 		  (and latex-handle-escaped-parens
! 		       (setq escaped (looking-at "\\\\\\([])}]\\)"))))
! 	      (ignore-errors
! 	       (save-excursion
! 		 (when escaped
! 		   (goto-char (match-beginning 1)))
! 		 (latex-skip-close-parens)
! 		 (latex-backward-sexp-1)
! 		 (latex-find-indent 'virtual)))))
!        ;; Default (maybe an argument)
!        (let ((pos (point))
! 	     ;; Outdent \item if necessary.
! 	     (indent (if (looking-at tex-indent-item-re) (- tex-indent-item) 0))
! 	     up-list-pos)
! 	 ;; Find the previous point which determines our current indentation.
! 	 (condition-case err
! 	     (progn
  	       (latex-backward-sexp-1)
! 	       (while (> (current-column) (current-indentation))
! 		 (latex-backward-sexp-1)))
! 	   (scan-error
! 	    (setq up-list-pos (nth 2 err))))
! 	 (cond
! 	  ((= (point-min) pos) 0) ; We're really just indenting the first line.
! 	  ((integerp up-list-pos)
! 	   ;; Have to indent relative to the open-paren.
  	   (goto-char up-list-pos)
! 	   (if (and (not tex-indent-allhanging)
! 		    (save-excursion
! 		      ;; Make sure we're an argument to a macro and
! 		      ;; that the macro is at the beginning of a line.
! 		      (condition-case nil
! 			  (progn
! 			    (while (eq (char-syntax (char-after)) ?\()
! 			      (forward-sexp -1))
! 			    (and (eq (char-syntax (char-after)) ?/)
! 				 (progn (skip-chars-backward " \t&")
! 					(bolp))))
! 			(scan-error nil)))
! 		    (> pos (progn (latex-down-list)
! 				  (forward-comment (point-max))
! 				  (point))))
! 	       ;; Align with the first element after the open-paren.
! 	       (current-column)
! 	     ;; We're the first element after a hanging brace.
! 	     (goto-char up-list-pos)
! 	     (+ (if (and (looking-at "\\\\begin *{\\([^\n}]+\\)")
! 			 (member (match-string 1)
! 				 latex-noindent-environments))
! 		    0 tex-indent-basic)
! 		indent (latex-find-indent 'virtual))))
! 	  ;; We're now at the "beginning" of a line.
! 	  ((not (and (not virtual) (eq (char-after) ?\\)))
! 	   ;; Nothing particular here: just keep the same indentation.
! 	   (+ indent (current-column)))
! 	  ;; We're now looking at a macro call.
! 	  ((looking-at tex-indent-item-re)
! 	   ;; Indenting relative to an item, have to re-add the outdenting.
! 	   (+ indent (current-column) tex-indent-item))
! 	  (t
! 	   (let ((col (current-column)))
! 	     (if (or (not (eq (char-syntax (or (char-after pos) ?\s)) ?\())
! 		     ;; Can't be an arg if there's an empty line inbetween.
! 		     (save-excursion (re-search-forward "^[ \t]*$" pos t)))
! 		 ;; If the first char was not an open-paren, there's
! 		 ;; a risk that this is really not an argument to the
! 		 ;; macro at all.
! 		 (+ indent col)
! 	       (forward-sexp 1)
! 	       (if (< (line-end-position)
! 		      (save-excursion (forward-comment (point-max))
! 				      (point)))
! 		   ;; we're indenting the first argument.
! 		   (min (current-column) (+ tex-indent-arg col))
! 		 (skip-syntax-forward " ")
! 		 (current-column)))))))))))
  ;;; DocTeX support
  
  (defun doctex-font-lock-^^A ()