X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/47854a55680b5809811caf72f66ecbe8289c2855..fb10ee4af92130504c60b61e7ff1238ac18a7857:/lisp/textmodes/tex-mode.el diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index 048c792904..7443db7d03 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: @@ -66,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 @@ -100,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." @@ -108,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." @@ -116,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." @@ -124,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. @@ -134,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'." @@ -165,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." @@ -173,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." @@ -181,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. @@ -201,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; @@ -215,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 @@ -231,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 '("''" "\">" "\"'" ">>" "»") @@ -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 @@ -721,8 +719,7 @@ 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 @@ -730,8 +727,7 @@ Not smaller than the value set by `tex-suscript-height-minimum'." '((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) @@ -864,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 () @@ -906,6 +915,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 +1060,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) @@ -1058,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) @@ -1126,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) @@ -1269,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")) @@ -1404,6 +1422,95 @@ Puts point on a blank line between them." \n "\\item " >) +;;;; 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 ;;;; @@ -1475,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))) @@ -1501,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)) @@ -1521,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)))))) @@ -1685,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) @@ -1785,6 +1901,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,13 +2017,17 @@ 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) + ;; 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) @@ -1945,11 +2066,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 +2093,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 +2183,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 +2204,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 +2301,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 +2309,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 "(_")) @@ -2165,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, @@ -2476,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)) @@ -2518,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 ()