X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/c12ecb0af9679cc0e2fa0409931c34c035763469..ae8ba4092c4b068779cdd8a8705d4a7872bee9c4:/lisp/textmodes/tex-mode.el?ds=sidebyside diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index 048c792904..312a081992 100644 --- a/lisp/textmodes/tex-mode.el +++ b/lisp/textmodes/tex-mode.el @@ -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 +;; 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 ;; Free Software Foundation, Inc. ;; Maintainer: FSF @@ -12,10 +12,10 @@ ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,9 +23,7 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -502,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 @@ -906,6 +904,15 @@ Inherits `shell-mode-map' with a few additions.") ;; and we need to define it a second time for `autoload' to get the ;; proper docstring. (defalias 'tex-mode-internal (symbol-function 'tex-mode)) + +;; Suppress the byte-compiler warning about multiple definitions. +;; This is a) ugly, and b) cheating, but this was the last +;; 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))) + ;;;###autoload (defun tex-mode () "Major mode for editing files of input for TeX, LaTeX, or SliTeX. @@ -1042,7 +1049,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) @@ -1475,18 +1482,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))) @@ -1501,6 +1515,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)) @@ -1521,12 +1536,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)))))) @@ -1785,6 +1802,7 @@ If NOT-ALL is non-nil, save the `.dvi' file." (shell-quote-argument tex-start-commands)) " %f") t "%r.dvi") ("xdvi %r &" "%r.dvi") + ("\\doc-view \"%r.pdf\"" "%r.pdf") ("xpdf %r.pdf &" "%r.pdf") ("gv %r.ps &" "%r.ps") ("yap %r &" "%r.dvi") @@ -1900,11 +1918,11 @@ FILE is typically the output DVI or PDF file." (save-excursion (goto-char (point-max)) (and (re-search-backward - (concat - "(see the transcript file for additional information)" - "\\|^Output written on .*" - (regexp-quote (file-name-nondirectory file)) - " (.*)\\.") nil t) + (concat "(see the transcript file for additional information)" + "\\|^Output written on .*" + (regexp-quote (file-name-nondirectory file)) + " (.*)\\.") + nil t) (> (save-excursion (or (re-search-backward "\\[[0-9]+\\]" nil t) (point-min))) @@ -1945,11 +1963,15 @@ FILE is typically the output DVI or PDF file." (defvar tex-executable-cache nil) (defun tex-executable-exists-p (name) "Like `executable-find' but with a cache." - (let ((cache (assoc name tex-executable-cache))) - (if cache (cdr cache) - (let ((executable (executable-find name))) - (push (cons name executable) tex-executable-cache) - executable)))) + (let ((f (and (string-match "^\\\\\\([^ \t\n]+\\)" name) + (intern-soft (concat "tex-cmd-" (match-string 1 name)))))) + (if (fboundp f) + f + (let ((cache (assoc name tex-executable-cache))) + (if cache (cdr cache) + (let ((executable (executable-find name))) + (push (cons name executable) tex-executable-cache) + executable)))))) (defun tex-command-executable (cmd) (let ((s (if (stringp cmd) cmd (eval (car cmd))))) @@ -1968,6 +1990,26 @@ FILE is typically the output DVI or PDF file." (when (and (eq in t) (stringp out)) (not (tex-uptodate-p (format-spec out fspec))))))) +(defcustom tex-cmd-bibtex-args "--min-crossref=100" + "Extra args to pass to `bibtex' by default." + :type 'string + :version "23.1" + :group 'tex-run) + +(defun tex-format-cmd (format fspec) + "Like `format-spec' but adds user-specified args to the command. +Only applies the FSPEC to the args part of FORMAT." + (if (not (string-match "\\([^ /\\]+\\) " format)) + (format-spec format fspec) + (let* ((prefix (substring format 0 (match-beginning 0))) + (cmd (match-string 1 format)) + (args (substring format (match-end 0))) + (sym (intern-soft (format "tex-cmd-%s-args" cmd))) + (extra-args (and sym (symbol-value sym)))) + (concat prefix cmd + (if extra-args (concat " " extra-args)) + " " (format-spec args fspec))))) + (defun tex-compile-default (fspec) "Guess a default command given the `format-spec' FSPEC." ;; TODO: Learn to do latex+dvips! @@ -2038,7 +2080,10 @@ FILE is typically the output DVI or PDF file." ;; The history command was already applied to the same file, ;; so just reuse it. hist-cmd - (if cmds (format-spec (caar cmds) fspec)))))) + (if cmds (tex-format-cmd (caar cmds) fspec)))))) + +(defun tex-cmd-doc-view (file) + (pop-to-buffer (find-file-noselect file))) (defun tex-compile (dir cmd) "Run a command CMD on current TeX buffer's file in DIR." @@ -2056,14 +2101,24 @@ FILE is typically the output DVI or PDF file." (completing-read (format "Command [%s]: " (tex-summarize-command default)) (mapcar (lambda (x) - (list (format-spec (eval (car x)) fspec))) + (list (tex-format-cmd (eval (car x)) fspec))) tex-compile-commands) nil nil nil 'tex-compile-history default)))) (save-some-buffers (not compilation-ask-about-save) nil) - (if (tex-shell-running) - (tex-kill-job) - (tex-start-shell)) - (tex-send-tex-command cmd dir)) + (let ((f (and (string-match "^\\\\\\([^ \t\n]+\\)" cmd) + (intern-soft (concat "tex-cmd-" (match-string 1 cmd)))))) + (if (functionp f) + (condition-case nil + (let ((default-directory dir)) + (apply f (split-string-and-unquote + (substring cmd (match-end 0))))) + (wrong-number-of-arguments + (error "Wrong number of arguments to %s" + (substring (symbol-name f) 8)))) + (if (tex-shell-running) + (tex-kill-job) + (tex-start-shell)) + (tex-send-tex-command cmd dir)))) (defun tex-start-tex (command file &optional dir) "Start a TeX run, using COMMAND on FILE." @@ -2143,6 +2198,7 @@ for the error messages." (let* ((this-error (copy-marker begin-of-error)) (linenum (string-to-number (match-string 1))) (error-text (regexp-quote (match-string 3))) + try-filename (filename ;; Prefer --file-liner-error filename if we have it. (or errfilename @@ -2150,7 +2206,11 @@ for the error messages." (with-syntax-table tex-error-parse-syntax-table (backward-up-list 1) (skip-syntax-forward "(_") - (while (not (file-readable-p (thing-at-point 'filename))) + (while (not + (and (setq try-filename (thing-at-point + 'filename)) + (not (string= "" try-filename)) + (file-readable-p try-filename))) (skip-syntax-backward "(_") (backward-up-list 1) (skip-syntax-forward "(_")) @@ -2476,10 +2536,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)) @@ -2518,97 +2579,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 ()