(BUFFER_POS_REACHED_P): Return true if pos reached and
[bpt/emacs.git] / lisp / textmodes / tex-mode.el
index 68b408a..4b878f4 100644 (file)
@@ -1,7 +1,7 @@
 ;;; tex-mode.el --- TeX, LaTeX, and SliTeX mode commands -*- coding: utf-8 -*-
 
-;; Copyright (C) 1985,86,89,92,94,95,96,97,98,1999,2002,2003
-;;       Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1989, 1992, 1994, 1995, 1996, 1997, 1998, 1999,
+;;   2002, 2003, 2004  Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: tex
@@ -127,7 +127,7 @@ 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'."
   :type 'string
   :group 'tex-run
-  :version "21.4")
+  :version "22.1")
 
 ;;;###autoload
 (defcustom tex-start-commands "\\nonstopmode\\input"
@@ -139,7 +139,7 @@ If nil, no commands are used.  See the documentation of `tex-command'."
                       "\\nonstopmode\\input")
                (string :tag "String at your choice"))
   :group 'tex-run
-  :version "21.4")
+  :version "22.1")
 
 (defvar latex-standard-block-names
   '("abstract"         "array"         "center"        "description"
@@ -196,7 +196,11 @@ use."
   :group 'tex-view)
 
 ;;;###autoload
-(defcustom tex-dvi-view-command '(if (eq window-system 'x) "xdvi" "dvi2tty * | cat -s")
+(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.
 If it is a string, that specifies the command directly.
 If this string contains an asterisk (`*'), that is replaced by the file name;
@@ -453,7 +457,8 @@ An alternative value is \" . \", if you use a font with a narrow period."
                      '("input" "include" "includeonly" "bibliography"
                        "epsfig" "psfig" "epsf" "nofiles" "usepackage"
                        "documentstyle" "documentclass" "verbatiminput"
-                       "includegraphics" "includegraphics*")
+                       "includegraphics" "includegraphics*"
+                       "url" "nolinkurl")
                      t))
           ;; Miscellany.
           (slash "\\\\")
@@ -463,6 +468,10 @@ 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
@@ -495,6 +504,11 @@ An alternative value is \" . \", if you use a font with a narrow period."
         1 font-lock-function-name-face))))
   "Subdued expressions to highlight in TeX modes.")
 
+(defun tex-font-lock-append-prop (prop)
+  (unless (memq (get-text-property (match-end 1) 'face)
+               '(font-lock-comment-face tex-verbatim-face))
+    prop))
+
 (defconst tex-font-lock-keywords-2
   (append tex-font-lock-keywords-1
    (eval-when-compile
@@ -548,16 +562,19 @@ An alternative value is \" . \", if you use a font with a narrow period."
        ;;
        ;; Font environments.  It seems a bit dubious to use `bold' etc. faces
        ;; since we might not be able to display those fonts.
-       (list (concat slash bold " *" arg) 2 '(quote bold) 'append)
-       (list (concat slash italic " *" arg) 2 '(quote italic) 'append)
+       (list (concat slash bold " *" arg) 2
+             '(tex-font-lock-append-prop 'bold) 'append)
+       (list (concat slash italic " *" arg) 2
+             '(tex-font-lock-append-prop 'italic) 'append)
        ;; (list (concat slash type arg) 2 '(quote bold-italic) 'append)
        ;;
        ;; Old-style bf/em/it/sl.  Stop at `\\' and un-escaped `&', for tables.
        (list (concat "\\\\\\(em\\|it\\|sl\\)\\>" args)
-             2 '(quote italic) 'append)
+             2 '(tex-font-lock-append-prop 'italic) 'append)
        ;; This is separate from the previous one because of cases like
        ;; {\em foo {\bf bar} bla} where both match.
-       (list (concat "\\\\bf\\>" args) 1 '(quote bold) 'append)))))
+       (list (concat "\\\\\\(bf\\)\\>" args)
+             2 '(tex-font-lock-append-prop 'bold) 'append)))))
    "Gaudy expressions to highlight in TeX modes.")
 
 (defun tex-font-lock-suscript (pos)
@@ -599,11 +616,16 @@ An alternative value is \" . \", if you use a font with a narrow period."
 (defvar tex-font-lock-syntactic-keywords
   (let ((verbs (regexp-opt tex-verbatim-environments t)))
     `((,(concat "^\\\\begin *{" verbs "}.*\\(\n\\)") 2 "|")
-      (,(concat "^\\\\end *{" verbs "}\\(.?\\)") 2
-       (unless (<= (match-beginning 0) (point-min))
-        (put-text-property (1- (match-beginning 0)) (match-beginning 0)
-                           'syntax-table (string-to-syntax "|"))
-        "<"))
+      ;; 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
+      (,(concat "^\\(\\\\\\)end *{" verbs "}\\(.?\\)") (1 "|") (3 "<"))
       ;; ("^\\(\\\\\\)begin *{comment}" 1 "< b")
       ;; ("^\\\\end *{comment}.*\\(\n\\)" 1 "> b")
       ("\\\\verb\\**\\([^a-z@*]\\)" 1 "\""))))
@@ -771,8 +793,10 @@ Inherits `shell-mode-map' with a few additions.")
                                    "part" "chapter" "newcommand"
                                    "renewcommand") 'words)
                      "\\|NeedsTeXFormat{LaTeX")))
-                 (if (looking-at
-                      "document\\(style\\|class\\)\\(\\[.*\\]\\)?{slides}")
+                 (if (and (looking-at
+                           "document\\(style\\|class\\)\\(\\[.*\\]\\)?{slides}")
+                          ;; SliTeX is almost never used any more nowadays.
+                          (tex-executable-exists-p slitex-run-command))
                      'slitex-mode
                    'latex-mode)
                'plain-tex-mode))))
@@ -1141,10 +1165,14 @@ on the line for the invalidity you want to see."
                                         'occur-target tem)))))
            (goto-char prev-end))))
       (with-current-buffer standard-output
-       (if (eq num-matches 0)
-           (insert "None!\n"))
-       (if (interactive-p)
-           (message "%d mismatches found" num-matches))))))
+       (let ((no-matches (zerop num-matches)))
+         (if no-matches
+             (insert "None!\n"))
+         (if (interactive-p)
+             (message (cond (no-matches "No mismatches found")
+                            ((= num-matches 1) "1 mismatch found")
+                            (t "%d mismatches found"))
+                      num-matches)))))))
 
 (defun tex-validate-region (start end)
   "Check for mismatched braces or $'s in region.
@@ -1162,14 +1190,14 @@ area if a mismatch is found."
              (forward-sexp 1))
            ;; Now check that like matches like.
            (goto-char start)
-           (while (progn (skip-syntax-forward "^(")
-                         (not (eobp)))
-             (let ((match (matching-paren (following-char))))
-               (save-excursion
+           (while (re-search-forward "\\s(" nil t)
+             (save-excursion
+               (let ((pos (match-beginning 0)))
+                 (goto-char pos)
                  (forward-sexp 1)
-                 (or (= (preceding-char) match)
-                     (error "Mismatched parentheses"))))
-             (forward-char 1)))
+                 (or (eq (preceding-char) (cdr (syntax-after pos)))
+                     (eq (char-after pos) (cdr (syntax-after (1- (point)))))
+                     (error "Mismatched parentheses"))))))
        (error
         (skip-syntax-forward " .>")
         (setq failure-point (point)))))
@@ -1215,8 +1243,13 @@ A prefix arg inhibits the checking."
 (defvar latex-block-default "enumerate")
 
 (defvar latex-block-args-alist
-  '(("array" nil ?\{ (skeleton-read "[options]: ") ?\})
-    ("tabular" nil ?\{ (skeleton-read "[options]: ") ?\}))
+  '(("array" nil ?\{ (skeleton-read "Format: ") ?\})
+    ("tabular" nil ?\{ (skeleton-read "Format: ") ?\})
+    ("minipage" nil ?\{ (skeleton-read "Size: ") ?\})
+    ("picture" nil ?\( (skeleton-read "SizeX,SizeY: ") ?\))
+    ;; FIXME: This is right for Prosper, but not for seminar.
+    ;; ("slide" nil ?\{ (skeleton-read "Title: ") ?\})
+    )
   "Skeleton element to use for arguments to particular environments.
 Every element of the list has the form (NAME . SKEL-ELEM) where NAME is
 the name of the environment and SKEL-ELEM is an element to use in
@@ -1225,8 +1258,11 @@ a skeleton (see `skeleton-insert').")
 (defvar latex-block-body-alist
   '(("enumerate" nil '(latex-insert-item) > _)
     ("itemize" nil '(latex-insert-item) > _)
-    ("table" nil "\\caption{" > - "}" > \n _)
-    ("figure" nil  > _ \n "\\caption{" > _ "}" >))
+    ("table" nil "\\caption{" > (skeleton-read "Caption: ") "}" > \n
+     '(if (and (boundp 'reftex-mode) reftex-mode) (reftex-label "table"))
+     \n _)
+    ("figure" nil  > _ \n "\\caption{" > (skeleton-read "Caption: ") "}" > \n
+     '(if (and (boundp 'reftex-mode) reftex-mode) (reftex-label "table"))))
   "Skeleton element to use for the body of particular environments.
 Every element of the list has the form (NAME . SKEL-ELEM) where NAME is
 the name of the environment and SKEL-ELEM is an element to use in
@@ -1250,7 +1286,8 @@ Puts point on a blank line between them."
     choice)
   \n "\\begin{" str "}"
   (cdr (assoc str latex-block-args-alist))
-  > \n (or (cdr (assoc str latex-block-body-alist)) '(nil > _)) \n
+  > \n (or (cdr (assoc str latex-block-body-alist)) '(nil > _))
+  (unless (bolp) '\n)
   "\\end{" str "}" > \n)
 
 (define-skeleton latex-insert-item
@@ -1314,7 +1351,9 @@ Mark is left at original location."
       (when (eq (char-after) ?{)
        (let ((newpos (point)))
          (when (ignore-errors (backward-sexp 1) t)
-           (if (looking-at "\\\\end\\>")
+           (if (or (looking-at "\\\\end\\>")
+                   ;; In case the \\ ends a verbatim section.
+                   (and (looking-at "end\\>") (eq (char-before) ?\\)))
                (tex-last-unended-begin)
              (goto-char newpos))))))))
 
@@ -1446,6 +1485,8 @@ Mark is left at original location."
 ;; The utility functions:
 
 (define-derived-mode tex-shell shell-mode "TeX-Shell"
+  (set (make-local-variable 'compilation-parse-errors-function)
+       'tex-compilation-parse-errors)
   (compilation-shell-minor-mode t))
 
 ;;;###autoload
@@ -1454,10 +1495,12 @@ Mark is left at original location."
       (make-comint
        "tex-shell"
        (or tex-shell-file-name (getenv "ESHELL") shell-file-name)
-       nil)
+       nil
+       ;; Specify an interactive shell, to make sure it prompts.
+       "-i")
     (let ((proc (get-process "tex-shell")))
       (set-process-sentinel proc 'tex-shell-sentinel)
-      (process-kill-without-query proc)
+      (set-process-query-on-exit-flag proc nil)
       (tex-shell)
       (while (zerop (buffer-size))
        (sleep-for 1)))))
@@ -1592,7 +1635,7 @@ If NOT-ALL is non-nil, save the `.dvi' file."
     ("texindex %r.??")
     ("dvipdfm %r" "%r.dvi" "%r.pdf")
     ("dvipdf %r" "%r.dvi" "%r.pdf")
-    ("dvips %r" "%r.dvi" "%r.ps")
+    ("dvips -o %r.ps %r" "%r.dvi" "%r.ps")
     ("ps2pdf %r.ps" "%r.ps" "%r.pdf")
     ("gv %r.ps &" "%r.ps")
     ("gv %r.pdf &" "%r.pdf")
@@ -1654,9 +1697,12 @@ of the current buffer."
   (let* ((file (or tex-main-file
                   ;; Compatibility with AUCTeX.
                   (with-no-warnings
-                   (when (and (boundp 'TeX-master) (stringp TeX-master))
-                     (make-local-variable 'tex-main-file)
-                     (setq tex-main-file TeX-master)))
+                   (when (boundp 'TeX-master)
+                     (cond ((stringp TeX-master)
+                            (make-local-variable 'tex-main-file)
+                            (setq tex-main-file TeX-master))
+                           ((and (eq TeX-master t) buffer-file-name)
+                            (file-relative-name buffer-file-name)))))
                   ;; Try to guess the main file.
                   (if (not buffer-file-name)
                       (error "Buffer is not associated with any file")
@@ -1730,7 +1776,7 @@ FILE is typically the output DVI or PDF file."
           (when (file-newer-than-file-p f file)
             (setq uptodate nil)))))
      uptodate)))
-    
+
 
 (autoload 'format-spec "format-spec")
 
@@ -1761,7 +1807,7 @@ FILE is typically the output DVI or PDF file."
        (not (tex-uptodate-p (format-spec out fspec)))))))
 
 (defun tex-compile-default (fspec)
-  "Guess a default command given the format-spec FSPEC."
+  "Guess a default command given the `format-spec' FSPEC."
   ;; TODO: Learn to do latex+dvips!
   (let ((cmds nil)
        (unchanged-in nil))
@@ -1771,6 +1817,9 @@ FILE is typically the output DVI or PDF file."
        (if (tex-command-active-p cmd fspec)
            (push cmd cmds)
          (push (nth 1 cmd) unchanged-in))))
+    ;; If no command seems to be applicable, arbitrarily pick the first one.
+    (unless cmds
+      (setq cmds (list (car tex-compile-commands))))
     ;; Remove those commands whose input was considered stable for
     ;; some other command (typically if (t . "%.pdf") is inactive
     ;; then we're using pdflatex and the fact that the dvi file
@@ -1879,8 +1928,6 @@ FILE is typically the output DVI or PDF file."
     (let (shell-dirtrack-verbose)
       (tex-send-command tex-shell-cd-command dir)))
   (with-current-buffer (process-buffer (tex-send-command cmd))
-    (make-local-variable 'compilation-parse-errors-function)
-    (setq compilation-parse-errors-function 'tex-compilation-parse-errors)
     (setq compilation-last-buffer (current-buffer))
     (compilation-forget-errors)
     ;; Don't parse previous compilations.
@@ -1912,7 +1959,6 @@ since TeX does not put file names and line numbers on the same line as
 for the error messages."
   (require 'thingatpt)
   (setq compilation-error-list nil)
-  (message "Parsing error messages...")
   (let ((default-directory             ; Perhaps dir has changed meanwhile.
          (file-name-directory (buffer-file-name tex-last-buffer-texed)))
        found-desired (num-errors-found 0)
@@ -1927,8 +1973,8 @@ for the error messages."
                        end-of-error (match-end 0)))
                (re-search-forward
                 "^l\\.\\([0-9]+\\) \\(\\.\\.\\.\\)?\\(.*\\)$" nil 'move))
-      (let* ((this-error (set-marker (make-marker) begin-of-error))
-            (linenum (string-to-int (match-string 1)))
+      (let* ((this-error (copy-marker begin-of-error))
+            (linenum (string-to-number (match-string 1)))
             (error-text (regexp-quote (match-string 3)))
             (filename
              (save-excursion
@@ -1944,21 +1990,22 @@ for the error messages."
              (or (null last-filename)
                  (not (string-equal last-filename filename))))
             (error-location
-             (save-excursion
-               (if (equal filename (concat tex-zap-file ".tex"))
-                   (set-buffer tex-last-buffer-texed)
-                 (set-buffer (find-file-noselect filename)))
-               (if new-file
-                   (progn (goto-line linenum) (setq last-position nil))
-                 (goto-char last-position)
-                 (forward-line (- linenum last-linenum)))
-               ;; first try a forward search for the error text,
-               ;; then a backward search limited by the last error.
-               (let ((starting-point (point)))
-                 (or (re-search-forward error-text nil t)
-                     (re-search-backward error-text last-position t)
-                     (goto-char starting-point)))
-               (point-marker))))
+             (with-current-buffer
+                 (if (equal filename (concat tex-zap-file ".tex"))
+                     tex-last-buffer-texed
+                   (find-file-noselect filename))
+               (save-excursion
+                 (if new-file
+                     (progn (goto-line linenum) (setq last-position nil))
+                   (goto-char last-position)
+                   (forward-line (- linenum last-linenum)))
+                 ;; first try a forward search for the error text,
+                 ;; then a backward search limited by the last error.
+                 (let ((starting-point (point)))
+                   (or (re-search-forward error-text nil t)
+                       (re-search-backward error-text last-position t)
+                       (goto-char starting-point)))
+                 (point-marker)))))
        (goto-char this-error)
        (if (and compilation-error-list
                 (or (and find-at-least
@@ -1977,8 +2024,7 @@ for the error messages."
                      compilation-error-list))
          (goto-char end-of-error)))))
   (set-marker compilation-parsing-end (point))
-  (setq compilation-error-list (nreverse compilation-error-list))
-  (message "Parsing error messages...done"))
+  (setq compilation-error-list (nreverse compilation-error-list)))
 \f
 ;;; The commands:
 
@@ -2256,6 +2302,7 @@ Runs the shell command defined by `tex-show-queue-command'."
 (defvar tex-indent-basic 2)
 (defvar tex-indent-item tex-indent-basic)
 (defvar tex-indent-item-re "\\\\\\(bib\\)?item\\>")
+(defvar latex-noindent-environments '("document"))
 
 (defvar tex-latex-indent-syntax-table
   (let ((st (make-syntax-table tex-mode-syntax-table)))
@@ -2306,7 +2353,6 @@ There might be text before point."
              (latex-find-indent 'virtual))))
      ;; Default (maybe an argument)
      (let ((pos (point))
-          (char (char-after))
           ;; Outdent \item if necessary.
           (indent (if (looking-at tex-indent-item-re) (- tex-indent-item) 0))
           up-list-pos)
@@ -2324,6 +2370,17 @@ There might be text before point."
         ;; 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))))
@@ -2331,18 +2388,24 @@ There might be text before point."
             (current-column)
           ;; We're the first element after a hanging brace.
           (goto-char up-list-pos)
-          (+ indent tex-indent-basic (latex-find-indent 'virtual))))
+          (+ (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.
+       ((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 (null char) (not (eq (char-syntax char) ?\()))
+          (if (or (not (eq (char-syntax (or (char-after pos) ?\ )) ?\())
+                  ;; 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.
@@ -2417,5 +2480,5 @@ There might be text before point."
 
 (provide 'tex-mode)
 
-;;; arch-tag: c0a680b1-63aa-4547-84b9-4193c29c0080
+;; arch-tag: c0a680b1-63aa-4547-84b9-4193c29c0080
 ;;; tex-mode.el ends here