Bind "C-c ]" to block-closing commands in several modes.
[bpt/emacs.git] / lisp / textmodes / tex-mode.el
index 0a89a34..08f8257 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, 2010
 ;;   Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 
 ;;;###autoload
 (defcustom tex-shell-file-name nil
-  "*If non-nil, the shell file name to run in the subshell used to run TeX."
+  "If non-nil, the shell file name to run in the subshell used to run TeX."
   :type '(choice (const :tag "None" nil)
                 string)
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-directory "."
-  "*Directory in which temporary files are written.
+(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
 `\\input' commands with relative directories."
@@ -84,7 +84,7 @@ if it matches the first line of the file,
 
 ;;;###autoload
 (defcustom tex-main-file nil
-  "*The main TeX source file which includes this buffer's file.
+  "The main TeX source file which includes this buffer's file.
 The command `tex-file' runs TeX on the file specified by `tex-main-file'
 if the variable is non-nil."
   :type '(choice (const :tag "None" nil)
@@ -93,37 +93,37 @@ if the variable is non-nil."
 
 ;;;###autoload
 (defcustom tex-offer-save t
-  "*If non-nil, ask about saving modified buffers before \\[tex-file] is run."
+  "If non-nil, ask about saving modified buffers before \\[tex-file] is run."
   :type 'boolean
   :group 'tex-file)
 
 ;;;###autoload
-(defcustom tex-run-command "tex"
-  "*Command used to run TeX subjob.
+(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."
   :type 'string
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom latex-run-command "latex"
-  "*Command used to run LaTeX subjob.
+(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."
   :type 'string
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom slitex-run-command "slitex"
-  "*Command used to run SliTeX subjob.
+(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."
   :type 'string
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-start-options ""
-  "*TeX options to use when starting TeX.
+(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.
 If nil, TeX runs with no options.  See the documentation of `tex-command'."
@@ -132,8 +132,8 @@ If nil, TeX runs with no options.  See the documentation of `tex-command'."
   :version "22.1")
 
 ;;;###autoload
-(defcustom tex-start-commands "\\nonstopmode\\input"
-  "*TeX commands to use when starting TeX.
+(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'."
   :type '(radio (const :tag "Interactive \(nil\)" nil)
@@ -157,30 +157,30 @@ If nil, no commands are used.  See the documentation of `tex-command'."
 
 ;;;###autoload
 (defcustom latex-block-names nil
-  "*User defined LaTeX block names.
+  "User defined LaTeX block names.
 Combined with `latex-standard-block-names' for minibuffer completion."
   :type '(repeat string)
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-bibtex-command "bibtex"
-  "*Command used by `tex-bibtex-file' to gather bibliographic data.
+(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."
   :type 'string
   :group 'tex-run)
 
 ;;;###autoload
-(defcustom tex-dvi-print-command "lpr -d"
-  "*Command used by \\[tex-print] to print a .dvi file.
+(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."
   :type 'string
   :group 'tex-view)
 
 ;;;###autoload
-(defcustom tex-alt-dvi-print-command "lpr -d"
-  "*Command used by \\[tex-print] with a prefix arg to print a .dvi file.
+(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,11 +199,11 @@ use."
 
 ;;;###autoload
 (defcustom tex-dvi-view-command
-  '(cond
-    ((eq window-system 'x) "xdvi")
-    ((eq window-system 'w32) "yap")
-    (t "dvi2tty * | cat -s"))
-  "*Command used by \\[tex-view] to display a `.dvi' file.
+  `(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;
 otherwise, the file name, preceded by a space, is added at the end.
@@ -213,15 +213,15 @@ 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"
-  "*Command used by \\[tex-show-print-queue] to show the print queue.
+(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
   :group 'tex-view)
 
 ;;;###autoload
 (defcustom tex-default-mode 'latex-mode
-  "*Mode to enter for a new file that might be either TeX or LaTeX.
+  "Mode to enter for a new file that might be either TeX or LaTeX.
 This variable is used when it can't be determined whether the file
 is plain TeX or LaTeX or what because the file contains no commands.
 Normally set to either `plain-tex-mode' or `latex-mode'."
@@ -229,15 +229,15 @@ Normally set to either `plain-tex-mode' or `latex-mode'."
   :group 'tex)
 
 ;;;###autoload
-(defcustom tex-open-quote "``"
-  "*String inserted by typing \\[tex-insert-quote] to open a quotation."
+(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 "''"
-  "*String inserted by typing \\[tex-insert-quote] to close a quotation."
+(defcustom tex-close-quote (purecopy "''")
+  "String inserted by typing \\[tex-insert-quote] to close a quotation."
   :type 'string
   :options '("''" "\">" "\"'" ">>" "»")
   :group 'tex)
@@ -327,7 +327,7 @@ Set by \\[tex-region], \\[tex-buffer], and \\[tex-file].")
 ;;;;
 
 (defcustom latex-imenu-indent-string ". "
-  "*String to add repeated in front of nested sectional units for Imenu.
+  "String to add repeated in front of nested sectional units for Imenu.
 An alternative value is \" . \", if you use a font with a narrow period."
   :type 'string
   :group 'tex)
@@ -488,10 +488,6 @@ An alternative value is \" . \", if you use a font with a narrow period."
           ;; (arg "\\(?:{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)\\|\\\\[a-z*]+\\)"))
           (arg "{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)"))
       (list
-       ;; font-lock-syntactic-keywords causes the \ of \end{verbatim} to be
-       ;; highlighted as tex-verbatim face.  Let's undo that.
-       ;; This is ugly and brittle :-(  --Stef
-       '("^\\(\\\\\\)end" (1 (get-text-property (match-end 1) 'face) t))
        ;; display $$ math $$
        ;; We only mark the match between $$ and $$ because the $$ delimiters
        ;; themselves have already been marked (along with $..$) by syntactic
@@ -500,7 +496,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
@@ -642,28 +638,90 @@ An alternative value is \" . \", if you use a font with a narrow period."
 (put 'tex-verbatim-environments 'safe-local-variable
      (lambda (x) (null (delq t (mapcar 'stringp x)))))
 
-(defvar tex-font-lock-syntactic-keywords
-  '((eval . `(,(concat "^\\\\begin *{"
-                       (regexp-opt tex-verbatim-environments t)
-                       "}.*\\(\n\\)") 2 "|"))
-    ;; Technically, we'd like to put the "|" property on the \n preceding
-    ;; the \end, but this would have 2 disadvantages:
-    ;; 1 - it's wrong if the verbatim env is empty (the same \n is used to
-    ;;     start and end the fenced-string).
-    ;; 2 - font-lock considers the preceding \n as being part of the
-    ;;     preceding line, so things gets screwed every time the previous
-    ;;     line is re-font-locked on its own.
-    ;; There's a hack in tex-font-lock-keywords-1 to remove the verbatim
-    ;; face from the \ but C-M-f still jumps to the wrong spot :-(  --Stef
-    (eval . `(,(concat "^\\(\\\\\\)end *{"
-                       (regexp-opt tex-verbatim-environments t)
-                       "}\\(.?\\)") (1 "|") (3 "<")))
-    ;; ("^\\(\\\\\\)begin *{comment}" 1 "< b")
-    ;; ("^\\\\end *{comment}.*\\(\n\\)" 1 "> b")
+(eval-when-compile
+  (defconst tex-syntax-propertize-rules
+    (syntax-propertize-precompile-rules
     ("\\\\verb\\**\\([^a-z@*]\\)"
-     ;; Do it last, because it uses syntax-ppss which needs the
-     ;; syntax-table properties of previous entries.
-     1 (tex-font-lock-verb (match-end 1)))))
+      (1 (prog1 "\""
+           (tex-font-lock-verb
+            (match-beginning 0) (char-after (match-beginning 1))))))))
+
+  (defconst latex-syntax-propertize-rules
+    (syntax-propertize-precompile-rules
+     tex-syntax-propertize-rules
+     ("\\\\\\(?:end\\|begin\\) *\\({[^\n{}]*}\\)"
+      (1 (ignore
+          (tex-env-mark (match-beginning 0)
+                        (match-beginning 1) (match-end 1))))))))
+
+(defun tex-env-mark (cmd start end)
+  (when (= cmd (line-beginning-position))
+    (let ((arg (buffer-substring-no-properties (1+ start) (1- end))))
+      (when (member arg tex-verbatim-environments)
+        (if (eq ?b (char-after (1+ cmd)))
+            ;; \begin
+            (put-text-property (line-end-position)
+                               (line-beginning-position 2)
+                               'syntax-table (string-to-syntax "< c"))
+          ;; In the case of an empty verbatim env, the \n after the \begin is
+          ;; the same as the \n before the \end.  Lucky for us, the "> c"
+          ;; property associated to the \end will be placed afterwards, so it
+          ;; will override the "< c".
+          (put-text-property (1- cmd) cmd
+                             'syntax-table (string-to-syntax "> c"))
+          ;; The text between \end{verbatim} and \n is ignored, so we'll treat
+          ;; it as a comment.
+          (put-text-property end (min (1+ end) (line-end-position))
+                             'syntax-table (string-to-syntax "<"))))))
+  ;; Mark env args for possible electric pairing.
+  (unless (get-char-property (1+ start) 'text-clones) ;Already paired-up.
+    (put-text-property start end 'latex-env-pair t)))
+
+(define-minor-mode latex-electric-env-pair-mode
+  "Automatically update the \\end arg when editing the \\begin one.
+And vice-versa."
+  :lighter "/e"
+  (if latex-electric-env-pair-mode
+      (add-hook 'before-change-functions
+                #'latex-env-before-change nil 'local)
+    (remove-hook 'before-change-functions
+                 #'latex-env-before-change 'local)))
+
+(defun latex-env-before-change (start end)
+  (when (get-text-property start 'latex-env-pair)
+    (condition-case err
+        (with-silent-modifications
+          ;; Remove properties even if don't find a pair.
+          (remove-text-properties
+           (previous-single-property-change (1+ start) 'latex-env-pair)
+           (next-single-property-change start 'latex-env-pair)
+           '(latex-env-pair))
+          (unless (or (get-char-property start 'text-clones)
+                      (get-char-property (1+ start) 'text-clones)
+                      (save-excursion
+                        (goto-char start)
+                        (not (re-search-backward
+                              "\\\\\\(?:end\\|begi\\(n\\)\\) *{"
+                              (line-beginning-position) t))))
+            (let ((cmd-start (match-beginning 0))
+                  (type (match-end 1))  ;nil for \end, else \begin.
+                  (arg-start (1- (match-end 0))))
+              (save-excursion
+                (goto-char (match-end 0))
+                (when (and (looking-at "[^\n{}]*}")
+                           (> (match-end 0) end))
+                  (let ((arg-end (match-end 0)))
+                    (if (null type)     ;\end
+                        (progn (goto-char arg-end)
+                               (latex-forward-sexp -1) (forward-word 1))
+                      (goto-char cmd-start)
+                      (latex-forward-sexp 1)
+                      (let (forward-sexp-function) (backward-sexp)))
+                    (when (looking-at
+                           (regexp-quote (buffer-substring arg-start arg-end)))
+                      (text-clone-create arg-start arg-end))))))))
+      (scan-error nil)
+      (error (message "Error in latex-env-before-change: %s" err)))))
 
 (defun tex-font-lock-unfontify-region (beg end)
   (font-lock-default-unfontify-region beg end)
@@ -719,50 +777,43 @@ 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)
-  "Place syntax-table properties on the \verb construct.
-END is the position of the first delimiter after \verb."
-  (unless (nth 8 (syntax-ppss end))
+(defun tex-font-lock-verb (start delim)
+  "Place syntax table properties on the \verb construct.
+START is the position of the \\ and DELIM is the delimiter char."
     ;; Do nothing if the \verb construct is itself inside a comment or
     ;; verbatim env.
-    (save-excursion
+  (unless (nth 8 (save-excursion (syntax-ppss start)))
       ;; Let's find the end and mark it.
-      ;; We used to do it inside tex-font-lock-syntactic-face-function, but
-      ;; this leads to funny effects when jumping to the end of the buffer,
-      ;; because font-lock applies font-lock-syntactic-keywords to the whole
-      ;; preceding text but font-lock-syntactic-face-function only to the
-      ;; actually displayed text.
-      (goto-char end)
-      (let ((char (char-before)))
-        (skip-chars-forward (string ?^ char)) ;; Use `end' ?
-        (when (eq (char-syntax (preceding-char)) ?/)
-          (put-text-property (1- (point)) (point) 'syntax-table '(1)))
+    ;; This may span more than a single line, but we don't bother
+    ;; placing a syntax-multiline property since such multiline verbs aren't
+    ;; valid anyway.
+    (skip-chars-forward (string ?^ delim))
         (unless (eobp)
-          (put-text-property (point) (1+ (point)) 'syntax-table '(7))
-          ;; Cause the rest of the buffer to be re-fontified.
-          ;; (remove-text-properties (1+ (point)) (point-max) '(fontified))
-          )))
-    "\""))
+      (when (eq (char-syntax (preceding-char)) ?/)
+        (put-text-property (1- (point)) (point)
+                           'syntax-table (string-to-syntax ".")))
+      (put-text-property (point) (1+ (point))
+                         'syntax-table (string-to-syntax "\"")))))
 
 ;; Use string syntax but math face for $...$.
 (defun tex-font-lock-syntactic-face-function (state)
   (let ((char (nth 3 state)))
     (cond
-     ((not char) font-lock-comment-face)
+     ((not char)
+      (if (eq 2 (nth 7 state)) tex-verbatim-face font-lock-comment-face))
      ((eq char ?$) tex-math-face)
+     ;; A \verb element.
      (t tex-verbatim-face))))
 
 \f
@@ -810,6 +861,12 @@ END is the position of the first delimiter after \verb."
     (define-key map "\C-c\C-c" 'tex-compile)
     (define-key map "\C-c\C-i" 'tex-bibtex-file)
     (define-key map "\C-c\C-o" 'latex-insert-block)
+
+    ;; Redundant keybindings, for consistency with SGML mode.
+    (define-key map "\C-c\C-t" 'latex-insert-block)
+    (define-key map "\C-c]" 'latex-close-block)
+    (define-key map "\C-c/" 'latex-close-block)
+
     (define-key map "\C-c\C-e" 'latex-close-block)
     (define-key map "\C-c\C-u" 'tex-goto-last-unclosed-latex-block)
     (define-key map "\C-c\C-m" 'tex-feed-input)
@@ -862,6 +919,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 ()
@@ -910,8 +980,8 @@ Inherits `shell-mode-map' with a few additions.")
 ;; remaining warning from byte-compiling all of Emacs...
 (eval-when-compile
   (setq byte-compile-function-environment
-        (delq (assq 'tex-mode byte-compile-function-environment)
-              byte-compile-function-environment)))
+       (delq (assq 'tex-mode byte-compile-function-environment)
+             byte-compile-function-environment)))
 
 ;;;###autoload
 (defun tex-mode ()
@@ -1049,7 +1119,7 @@ subshell is initiated, `tex-shell-hook' is run."
                "\\>\\|\\\\[a-z]*" (regexp-opt '("space" "skip" "page") t)
                "\\>\\)"))
   (setq paragraph-separate
-       (concat "[\f]\\|[ \t]*\\($\\|"
+       (concat "[\f%]\\|[ \t]*\\($\\|"
                "\\\\[][]\\|"
                "\\\\" (regexp-opt (append
                                    (mapcar 'car latex-section-alist)
@@ -1065,6 +1135,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 +1205,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)
@@ -1147,10 +1217,9 @@ Entering SliTeX mode runs the hook `text-mode-hook', then the hook
         (font-lock-syntactic-face-function
          . tex-font-lock-syntactic-face-function)
         (font-lock-unfontify-region-function
-         . tex-font-lock-unfontify-region)
-        (font-lock-syntactic-keywords
-         . tex-font-lock-syntactic-keywords)
-        (parse-sexp-lookup-properties . t)))
+         . tex-font-lock-unfontify-region)))
+  (set (make-local-variable 'syntax-propertize-function)
+       (syntax-propertize-rules latex-syntax-propertize-rules))
   ;; TABs in verbatim environments don't do what you think.
   (set (make-local-variable 'indent-tabs-mode) nil)
   ;; Other vars that should be buffer-local.
@@ -1276,7 +1345,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 +1480,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 +1640,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 +1673,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 +1694,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 +1860,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 +2081,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 +2367,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 +2386,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 +2700,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 (expand-file-name (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 +2743,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 ()
@@ -2672,15 +2865,15 @@ There might be text before point."
             ;; syntax-table can't deal with.  We could turn it
             ;; into a non-comment, or use `\n%' or `%^' as the comment.
             ;; Instead, we include it in the ^^A comment.
-            (eval-when-compile (string-to-syntax "< b"))
-          (eval-when-compile (string-to-syntax ">"))))
+             (string-to-syntax "< b")
+           (string-to-syntax ">")))
        (let ((end (line-end-position)))
          (if (< end (point-max))
              (put-text-property
               end (1+ end)
               'syntax-table
-              (eval-when-compile (string-to-syntax "> b")))))
-       (eval-when-compile (string-to-syntax "< b")))))
+               (string-to-syntax "> b"))))
+        (string-to-syntax "< b"))))
 
 (defun doctex-font-lock-syntactic-face-function (state)
   ;; Mark DocTeX documentation, which is parsed as a style A comment
@@ -2692,11 +2885,12 @@ There might be text before point."
       (tex-font-lock-syntactic-face-function state)
     font-lock-doc-face))
 
-(defvar doctex-font-lock-syntactic-keywords
-  (append
-   tex-font-lock-syntactic-keywords
-   ;; For DocTeX comment-in-doc.
-   `(("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A))))))
+(eval-when-compile
+  (defconst doctex-syntax-propertize-rules
+    (syntax-propertize-precompile-rules
+     latex-syntax-propertize-rules
+     ;; For DocTeX comment-in-doc.
+     ("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A))))))
 
 (defvar doctex-font-lock-keywords
   (append tex-font-lock-keywords
@@ -2710,12 +2904,12 @@ There might be text before point."
              (mapcar
               (lambda (x)
                 (case (car-safe x)
-                  (font-lock-syntactic-keywords
-                   (cons (car x) 'doctex-font-lock-syntactic-keywords))
                   (font-lock-syntactic-face-function
                    (cons (car x) 'doctex-font-lock-syntactic-face-function))
                   (t x)))
-              (cdr font-lock-defaults)))))
+              (cdr font-lock-defaults))))
+  (set (make-local-variable 'syntax-propertize-function)
+       (syntax-propertize-rules doctex-syntax-propertize-rules)))
 
 (run-hooks 'tex-mode-load-hook)