(latex-complete, latex-indent-or-complete): Remove.
[bpt/emacs.git] / lisp / textmodes / tex-mode.el
index 0a89a34..7443db7 100644 (file)
@@ -1,7 +1,7 @@
 ;;; tex-mode.el --- TeX, LaTeX, and SliTeX mode commands -*- coding: utf-8 -*-
 
 ;; Copyright (C) 1985, 1986, 1989, 1992, 1994, 1995, 1996, 1997, 1998
-;;   1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+;;   1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
 ;;   Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
@@ -64,7 +64,7 @@
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-directory "."
+(defcustom tex-directory (purecopy ".")
   "*Directory in which temporary files are written.
 You can make this `/tmp' if your TEXINPUTS has no relative directories in it
 and you don't try to apply \\[tex-region] or \\[tex-buffer] when there are
@@ -98,7 +98,7 @@ if the variable is non-nil."
   :group 'tex-file)
 
 ;;;###autoload
-(defcustom tex-run-command "tex"
+(defcustom tex-run-command (purecopy "tex")
   "*Command used to run TeX subjob.
 TeX Mode sets `tex-command' to this string.
 See the documentation of that variable."
@@ -106,7 +106,7 @@ See the documentation of that variable."
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom latex-run-command "latex"
+(defcustom latex-run-command (purecopy "latex")
   "*Command used to run LaTeX subjob.
 LaTeX Mode sets `tex-command' to this string.
 See the documentation of that variable."
@@ -114,7 +114,7 @@ See the documentation of that variable."
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom slitex-run-command "slitex"
+(defcustom slitex-run-command (purecopy "slitex")
   "*Command used to run SliTeX subjob.
 SliTeX Mode sets `tex-command' to this string.
 See the documentation of that variable."
@@ -122,7 +122,7 @@ See the documentation of that variable."
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-start-options ""
+(defcustom tex-start-options (purecopy "")
   "*TeX options to use when starting TeX.
 These immediately precede the commands in `tex-start-commands'
 and the input file name, with no separating space and are not shell-quoted.
@@ -132,7 +132,7 @@ If nil, TeX runs with no options.  See the documentation of `tex-command'."
   :version "22.1")
 
 ;;;###autoload
-(defcustom tex-start-commands "\\nonstopmode\\input"
+(defcustom tex-start-commands (purecopy "\\nonstopmode\\input")
   "*TeX commands to use when starting TeX.
 They are shell-quoted and precede the input file name, with a separating space.
 If nil, no commands are used.  See the documentation of `tex-command'."
@@ -163,7 +163,7 @@ Combined with `latex-standard-block-names' for minibuffer completion."
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-bibtex-command "bibtex"
+(defcustom tex-bibtex-command (purecopy "bibtex")
   "*Command used by `tex-bibtex-file' to gather bibliographic data.
 If this string contains an asterisk (`*'), that is replaced by the file name;
 otherwise, the file name, preceded by blank, is added at the end."
@@ -171,7 +171,7 @@ otherwise, the file name, preceded by blank, is added at the end."
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-dvi-print-command "lpr -d"
+(defcustom tex-dvi-print-command (purecopy "lpr -d")
   "*Command used by \\[tex-print] to print a .dvi file.
 If this string contains an asterisk (`*'), that is replaced by the file name;
 otherwise, the file name, preceded by blank, is added at the end."
@@ -179,7 +179,7 @@ otherwise, the file name, preceded by blank, is added at the end."
   :group 'tex-view)
 
 ;;;###autoload
-(defcustom tex-alt-dvi-print-command "lpr -d"
+(defcustom tex-alt-dvi-print-command (purecopy "lpr -d")
   "*Command used by \\[tex-print] with a prefix arg to print a .dvi file.
 If this string contains an asterisk (`*'), that is replaced by the file name;
 otherwise, the file name, preceded by blank, is added at the end.
@@ -199,10 +199,10 @@ use."
 
 ;;;###autoload
 (defcustom tex-dvi-view-command
-  '(cond
-    ((eq window-system 'x) "xdvi")
-    ((eq window-system 'w32) "yap")
-    (t "dvi2tty * | cat -s"))
+  `(cond
+    ((eq window-system 'x) ,(purecopy "xdvi"))
+    ((eq window-system 'w32) ,(purecopy "yap"))
+    (t ,(purecopy "dvi2tty * | cat -s")))
   "*Command used by \\[tex-view] to display a `.dvi' file.
 If it is a string, that specifies the command directly.
 If this string contains an asterisk (`*'), that is replaced by the file name;
@@ -213,7 +213,7 @@ If the value is a form, it is evaluated to get the command to use."
   :group 'tex-view)
 
 ;;;###autoload
-(defcustom tex-show-queue-command "lpq"
+(defcustom tex-show-queue-command (purecopy "lpq")
   "*Command used by \\[tex-show-print-queue] to show the print queue.
 Should show the queue(s) that \\[tex-print] puts jobs on."
   :type 'string
@@ -229,14 +229,14 @@ Normally set to either `plain-tex-mode' or `latex-mode'."
   :group 'tex)
 
 ;;;###autoload
-(defcustom tex-open-quote "``"
+(defcustom tex-open-quote (purecopy "``")
   "*String inserted by typing \\[tex-insert-quote] to open a quotation."
   :type 'string
   :options '("``" "\"<" "\"`" "<<" "«")
   :group 'tex)
 
 ;;;###autoload
-(defcustom tex-close-quote "''"
+(defcustom tex-close-quote (purecopy "''")
   "*String inserted by typing \\[tex-insert-quote] to close a quotation."
   :type 'string
   :options '("''" "\">" "\"'" ">>" "»")
@@ -500,7 +500,7 @@ An alternative value is \" . \", if you use a font with a narrow period."
        (list "\\$\\$\\([^$]+\\)\\$\\$" 1 'tex-math-face)
        ;; Heading args.
        (list (concat slash headings "\\*?" opt arg)
-            ;; If ARG ends up matching too much (if the {} don't match, f.ex)
+            ;; If ARG ends up matching too much (if the {} don't match, e.g.)
             ;; jit-lock will do funny things: when updating the buffer
             ;; the re-highlighting is only done locally so it will just
             ;; match the local line, but defer-contextually will
@@ -719,17 +719,15 @@ Not smaller than the value set by `tex-suscript-height-minimum'."
   '((t :inherit font-lock-string-face))
   "Face used to highlight TeX math expressions."
   :group 'tex)
-;; backward-compatibility alias
-(put 'tex-math-face 'face-alias 'tex-math)
+(define-obsolete-face-alias 'tex-math-face 'tex-math "22.1")
 (defvar tex-math-face 'tex-math)
 
 (defface tex-verbatim
   ;; '((t :inherit font-lock-string-face))
-  '((t :family "monospace"))
+  '((t :family "courier"))
   "Face used to highlight TeX verbatim environments."
   :group 'tex)
-;; backward-compatibility alias
-(put 'tex-verbatim-face 'face-alias 'tex-verbatim)
+(define-obsolete-face-alias 'tex-verbatim-face 'tex-verbatim "22.1")
 (defvar tex-verbatim-face 'tex-verbatim)
 
 (defun tex-font-lock-verb (end)
@@ -862,6 +860,19 @@ Inherits `shell-mode-map' with a few additions.")
     ,@tex-face-alist)
   "Alist of face and LaTeX font name for facemenu.")
 
+(defun tex-facemenu-add-face-function (face end)
+  (or (cdr (assq face tex-face-alist))
+      (or (and (consp face)
+              (consp (car face))
+              (null  (cdr face))
+              (eq major-mode 'latex-mode)
+              ;; This actually requires the `color' LaTeX package.
+              (cond ((eq (caar face) :foreground)
+                     (format "{\\color{%s} " (cadr (car face))))
+                    ((eq (caar face) :background)
+                     (format "\\colorbox{%s}{" (cadr (car face))))))
+         (error "Face %s not configured for %s mode" face mode-name))))
+
 ;; This would be a lot simpler if we just used a regexp search,
 ;; but then it would be too slow.
 (defun tex-guess-mode ()
@@ -1065,6 +1076,8 @@ subshell is initiated, `tex-shell-hook' is run."
   (add-hook 'fill-nobreak-predicate 'latex-fill-nobreak-predicate nil t)
   (set (make-local-variable 'indent-line-function) 'latex-indent)
   (set (make-local-variable 'fill-indent-according-to-mode) t)
+  (add-hook 'completion-at-point-functions
+            'latex-complete-data nil 'local)
   (set (make-local-variable 'outline-regexp) latex-outline-regexp)
   (set (make-local-variable 'outline-level) 'latex-outline-level)
   (set (make-local-variable 'forward-sexp-function) 'latex-forward-sexp)
@@ -1133,9 +1146,7 @@ Entering SliTeX mode runs the hook `text-mode-hook', then the hook
   (set (make-local-variable 'compare-windows-whitespace)
        'tex-categorize-whitespace)
   (set (make-local-variable 'facemenu-add-face-function)
-       (lambda (face end)
-        (or (cdr (assq face tex-face-alist))
-            (error "Face %s not configured for %s mode" face mode-name))))
+       'tex-facemenu-add-face-function)
   (set (make-local-variable 'facemenu-end-add-face) "}")
   (set (make-local-variable 'facemenu-remove-face-function) t)
   (set (make-local-variable 'font-lock-defaults)
@@ -1276,7 +1287,7 @@ on the line for the invalidity you want to see."
        (let ((no-matches (zerop num-matches)))
          (if no-matches
              (insert "None!\n"))
-         (if (interactive-p)
+         (if (called-interactively-p 'interactive)
              (message (cond (no-matches "No mismatches found")
                             ((= num-matches 1) "1 mismatch found")
                             (t "%d mismatches found"))
@@ -1411,6 +1422,95 @@ Puts point on a blank line between them."
   \n "\\item " >)
 
 \f
+;;;; LaTeX completion.
+
+(defvar latex-complete-bibtex-cache nil)
+
+(defun latex-string-prefix-p (str1 str2)
+  (eq t (compare-strings str1 nil nil str2 0 (length str1))))
+
+(defvar bibtex-reference-key)
+(declare-function reftex-get-bibfile-list "reftex-cite.el" ())
+
+(defun latex-complete-bibtex-keys ()
+  (when (bound-and-true-p reftex-mode)
+    (lambda (key pred action)
+      (let ((re (concat "^[ \t]*@\\([a-zA-Z]+\\)[ \t\n]*\\([{(][ \t\n]*\\)"
+                        (regexp-quote key)))
+            (files (reftex-get-bibfile-list))
+            keys)
+        (if (and (eq (car latex-complete-bibtex-cache)
+                     (reftex-get-bibfile-list))
+                 (latex-string-prefix-p (nth 1 latex-complete-bibtex-cache)
+                                        key))
+            ;; Use the cache.
+            (setq keys (nth 2 latex-complete-bibtex-cache))
+          (dolist (file files)
+            (with-current-buffer (find-file-noselect file)
+              (goto-char (point-min))
+              (while (re-search-forward re nil t)
+                (goto-char (match-end 2))
+                (when (and (not (member-ignore-case (match-string 1)
+                                                    '("c" "comment" "string")))
+                           (looking-at bibtex-reference-key))
+                  (push (match-string-no-properties 0) keys)))))
+          ;; Fill the cache.
+          (set (make-local-variable 'latex-complete-bibtex-cache)
+               (list files key keys)))
+        (complete-with-action action keys key pred)))))
+
+(defun latex-complete-envnames ()
+  (append latex-block-names latex-standard-block-names))
+
+(defun latex-complete-refkeys ()
+  (when (boundp 'reftex-docstruct-symbol)
+    (symbol-value reftex-docstruct-symbol)))
+
+(defvar latex-complete-alist
+  ;; TODO: Add \begin, \end, \ref, ...
+  '(("\\`\\\\\\(short\\)?cite\\'" . latex-complete-bibtex-keys)
+    ("\\`\\\\\\(begin\\|end\\)\\'" . latex-complete-envnames)
+    ("\\`\\\\[vf]?ref\\'" . latex-complete-refkeys)))
+
+(defun latex-complete-data ()
+  "Get completion-data at point."
+  (save-excursion
+    (let ((pt (point)))
+      (skip-chars-backward "^ {}\n\t\\\\")
+      (case (char-before)
+        ((nil ?\s ?\n ?\t ?\}) nil)
+        (?\\
+         ;; TODO: Complete commands.
+         nil)
+        (?\{
+         ;; Complete args to commands.
+         (let* ((cmd
+                 (save-excursion
+                   (forward-char -1)
+                   (skip-chars-backward " \n")
+                   (buffer-substring (point)
+                                     (progn
+                                       (skip-chars-backward "a-zA-Z@*")
+                                       (let ((n (skip-chars-backward "\\\\")))
+                                         (forward-char (* 2 (/ n 2))))
+                                       (point)))))
+                (start (point))
+                (_ (progn (goto-char pt) (skip-chars-backward "^," start)))
+                (comp-beg (point))
+                (_ (progn (goto-char pt) (skip-chars-forward "^, {}\n\t\\\\")))
+                (comp-end (point))
+                (table
+                 (funcall
+                  (let ((f (lambda () t)))
+                    (dolist (comp latex-complete-alist)
+                      (if (string-match (car comp) cmd)
+                          (setq f (cdr comp))))
+                    f))))
+           (if (eq table t)
+               ;; Unknown command.
+               nil
+             (list comp-beg comp-end table))))))))
+
 ;;;;
 ;;;; LaTeX syntax navigation
 ;;;;
@@ -1482,18 +1582,25 @@ Mark is left at original location."
     (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.
+;; Does not handle escaped parens when latex-handle-escaped-parens is nil.
 (defun latex-backward-sexp-1 ()
   "Like (backward-sexp 1) but aware of multi-char elements and escaped parens."
   (let ((pos (point))
        (forward-sexp-function))
     (backward-sexp 1)
-    (cond ((looking-at "\\\\\\(begin\\>\\|[[({]\\)")
+    (cond ((looking-at
+           (if latex-handle-escaped-parens
+               "\\\\\\(begin\\>\\|[[({]\\)"
+             "\\\\begin\\>"))
           (signal 'scan-error
                   (list "Containing expression ends prematurely"
                         (point) (prog1 (point) (goto-char pos)))))
-         ((looking-at "\\\\\\([])}]\\)")
+         ((and latex-handle-escaped-parens
+               (looking-at "\\\\\\([])}]\\)"))
           (tex-last-unended-eparen (match-string 1)))
          ((eq (char-after) ?{)
           (let ((newpos (point)))
@@ -1508,6 +1615,7 @@ Mark is left at original location."
 ;; begin/end blocks.
 ;; Needs to handle escaped parens for tex-validate-*.
 ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00038.html
+;; Does not handle escaped parens when latex-handle-escaped-parens is nil.
 (defun latex-forward-sexp-1 ()
   "Like (forward-sexp 1) but aware of multi-char elements and escaped parens."
   (let ((pos (point))
@@ -1528,12 +1636,14 @@ Mark is left at original location."
        (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 "\\\\[])}]")
+       ((and latex-handle-escaped-parens
+            (looking-back "\\\\[])}]"))
        (signal 'scan-error
                (list "Containing expression ends prematurely"
                      (- (point) 2) (prog1 (point)
                                      (goto-char pos)))))
-       ((looking-back "\\\\\\([({[]\\)")
+       ((and latex-handle-escaped-parens
+            (looking-back "\\\\\\([({[]\\)"))
        (tex-next-unmatched-eparen (match-string 1)))
        (t (goto-char newpos))))))
 
@@ -1692,8 +1802,7 @@ In the tex shell buffer this command behaves like `comint-send-input'."
   (setq directory (file-name-as-directory (expand-file-name directory)))
   (if (not (file-directory-p directory))
       (error "%s is not a directory" directory)
-    (save-excursion
-      (set-buffer buffer)
+    (with-current-buffer buffer
       (setq default-directory directory))))
 
 (defvar tex-send-command-modified-tick 0)
@@ -1914,7 +2023,11 @@ FILE is typically the output DVI or PDF file."
                          " (.*)\\.")
                  nil t)
                (> (save-excursion
-                    (or (re-search-backward "\\[[0-9]+\\]" nil t)
+                     ;; Usually page numbers are output as [N], but
+                     ;; I've already seen things like
+                     ;; [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}]
+                     (or (re-search-backward "\\[[0-9]+\\({[^}]*}\\)?\\]"
+                                             nil t)
                         (point-min)))
                   (save-excursion
                     (or (re-search-backward "Rerun" nil t)
@@ -2196,7 +2309,7 @@ for the error messages."
                    (with-syntax-table tex-error-parse-syntax-table
                      (backward-up-list 1)
                      (skip-syntax-forward "(_")
-                     (while (not 
+                     (while (not
                              (and (setq try-filename (thing-at-point
                                                       'filename))
                                   (not (string= "" try-filename))
@@ -2215,7 +2328,10 @@ for the error messages."
                    (find-file-noselect filename))
                (save-excursion
                  (if new-file
-                     (progn (goto-line linenum) (setq last-position nil))
+                     (progn
+                       (goto-char (point-min))
+                       (forward-line (1- linenum))
+                       (setq last-position nil))
                    (goto-char last-position)
                    (forward-line (- linenum last-linenum)))
                  ;; first try a forward search for the error text,
@@ -2526,10 +2642,11 @@ Runs the shell command defined by `tex-show-queue-command'."
   (if (tex-shell-running)
       (tex-kill-job)
     (tex-start-shell))
-  (let (shell-dirtrack-verbose
-       (tex-out-file
-         (tex-append (file-name-nondirectory (buffer-file-name)) ""))
-       (file-dir (file-name-directory (buffer-file-name))))
+  (let* (shell-dirtrack-verbose
+         (source-file (tex-main-file))
+         (tex-out-file
+          (tex-append (file-name-nondirectory source-file) ""))
+         (file-dir (file-name-directory source-file)))
     (tex-send-command tex-shell-cd-command file-dir)
     (tex-send-command tex-bibtex-command tex-out-file))
   (tex-display-shell))
@@ -2568,97 +2685,115 @@ Runs the shell command defined by `tex-show-queue-command'."
            (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."
-  (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 brace would be.
-     (and (eq (latex-syntax-after) ?\))
-         (ignore-errors
-           (save-excursion
-             (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.
+  (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 (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))))))))))
+          (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 ()