X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/108d6d47a903cff5d7ac7d83f126dda209c3eeb2..b717b0ee04ffdfd6c44c24cec77ef464fa8d4b95:/packages/auctex/tex.el diff --git a/packages/auctex/tex.el b/packages/auctex/tex.el new file mode 100644 index 000000000..b147474a0 --- /dev/null +++ b/packages/auctex/tex.el @@ -0,0 +1,5598 @@ +;;; tex.el --- Support for TeX documents. + +;; Copyright (C) 1985, 1986, 1987, 1991, 1993, 1994, 1996, 1997, 1999, +;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +;; Free Software Foundation, Inc. + +;; Maintainer: auctex-devel@gnu.org +;; Keywords: tex + +;; This file is part of AUCTeX. + +;; AUCTeX 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. + +;; AUCTeX is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with AUCTeX; see the file COPYING. If not, write to the Free +;; Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +;; 02110-1301, USA. + +;;; Commentary: + +;; This file provides AUCTeX support for plain TeX as well as basic +;; functions used by other AUCTeX modes (e.g. for LaTeX, Texinfo and +;; ConTeXt). + +;;; Code: + +(when (< emacs-major-version 21) + (error "AUCTeX requires Emacs 21 or later")) + +(require 'custom) +(require 'tex-site) +(eval-when-compile + (require 'cl)) + +(defgroup TeX-file nil + "Files used by AUCTeX." + :group 'AUCTeX) + +(defgroup TeX-command nil + "Calling external commands from AUCTeX." + :group 'AUCTeX) + +(defgroup LaTeX nil + "LaTeX support in AUCTeX." + :tag "LaTeX" + :group 'AUCTeX + :prefix "LaTeX-") + +(defgroup TeX-misc nil + "Various AUCTeX settings." + :group 'AUCTeX) + +;;; Site Customization +;; +;; The following variables are likely to need to be changed for your +;; site. You should do this with customize. Here is the beef: If you +;; want to print, TeX-print-command must be non-nil (if it is nil, +;; you'll get a complaint when using the print menu). If you want to +;; view the queue, TeX-queue-command needs to be non-nil (if it is +;; nil, it won't get mentioned in the menu). If TeX-printer-list is +;; nil, nothing else gets asked: the menu entries lead directly to the +;; respective commands. If those commands contain %p, the value of +;; TeX-printer-default gets inserted there, no questions asked. Now +;; if TeX-printer-list is non-nil, you'll always get asked which +;; printer you want to use. You can enter a configured printer from +;; TeX-printer-list, or an unknown one. The respective menus will +;; show all configured printers. Since you can enter unknown +;; printers, the printer name _must_ be set with %p in +;; TeX-print-command. + +;; How to print. + +(defcustom TeX-print-command "%(o?)dvips -P%p %r %s" + "*Command used to print a file. + +First `%p' is expanded to the printer name, then ordinary expansion is +performed as specified in `TeX-expand-list'. If it is nil, +then customization is requested." + :group 'TeX-command + :type '(choice (string :tag "Print command") + (const :tag "No print command customized" nil))) + +(defcustom TeX-command "tex" + "Command to run plain TeX." + :group 'TeX-command + :type 'string) + +(defcustom TeX-Omega-command "omega" + "Command to run plain TeX on Omega." + :group 'TeX-command + :type 'string) + +(defcustom LaTeX-command "latex" + "Command to run LaTeX." + :group 'TeX-command + :type 'string) + +(defcustom LaTeX-Omega-command "lambda" + "Command to run LaTeX on Omega." + :group 'TeX-command + :type 'string) + +(defcustom ConTeXt-engine nil + "Engine to use for --engine in the texexec command. +If nil, none is specified." + :group 'TeX-command + :type '(choice (const :tag "Unspecified" nil) + string)) + +(defcustom ConTeXt-Omega-engine TeX-Omega-command + "Engine to use for --engine in the texexec command in Omega mode. +If nil, none is specified." + :group 'TeX-command + :type '(choice (const :tag "Unspecified" nil) + string)) +;; At least in TeXLive 2009 ConTeXt does not support an omega option anymore. +(make-obsolete-variable 'ConTeXt-Omega-engine 'TeX-engine-alist) + +(defcustom TeX-queue-command "lpq -P%p" + "*Command used to show the status of a printer queue. + +First `%p' is expanded to the printer name, then ordinary expansion is +performed as specified in `TeX-expand-list'. If this is nil, +the printer has no corresponding command." + :group 'TeX-command + :type '(choice (string :tag "Queue check command") + (const :tag "No such command" nil))) + +(defcustom TeX-mode-hook nil + "A hook run in TeX mode buffers." + :type 'hook + :group 'TeX-misc) + +(defcustom AmS-TeX-mode-hook nil + "A hook run in AmS-TeX mode buffers." + :type 'hook + :group 'TeX-misc) + +;; This is the major configuration variable. Most sites will only +;; need to change the second string in each entry, which is the name +;; of a command to send to the shell. If you use other formatters +;; like AMSLaTeX or AMSTeX, you can add those to the list. See +;; TeX-expand-list for a description of the % escapes + +(defcustom TeX-command-list + `(("TeX" "%(PDF)%(tex) %`%S%(PDFout)%(mode)%' %t" + TeX-run-TeX nil + (plain-tex-mode ams-tex-mode texinfo-mode) :help "Run plain TeX") + ("LaTeX" "%`%l%(mode)%' %t" + TeX-run-TeX nil + (latex-mode doctex-mode) :help "Run LaTeX") + ;; Not part of standard TeX. + ("Makeinfo" "makeinfo %t" TeX-run-compile nil + (texinfo-mode) :help "Run Makeinfo with Info output") + ("Makeinfo HTML" "makeinfo --html %t" TeX-run-compile nil + (texinfo-mode) :help "Run Makeinfo with HTML output") + ("AmSTeX" "%(PDF)amstex %`%S%(PDFout)%(mode)%' %t" + TeX-run-TeX nil (ams-tex-mode) :help "Run AMSTeX") + ;; support for ConTeXt --pg + ;; first version of ConTeXt to support nonstopmode: 2003.2.10 + ("ConTeXt" "texexec --once --texutil %(execopts)%t" + TeX-run-TeX nil (context-mode) :help "Run ConTeXt once") + ("ConTeXt Full" "texexec %(execopts)%t" + TeX-run-TeX nil + (context-mode) :help "Run ConTeXt until completion") + ("BibTeX" "bibtex %s" TeX-run-BibTeX nil t :help "Run BibTeX") + ,(if (or window-system (getenv "DISPLAY")) + '("View" "%V" TeX-run-discard-or-function t t :help "Run Viewer") + '("View" "dvi2tty -q -w 132 %s" TeX-run-command t t + :help "Run Text viewer")) + ("Print" "%p" TeX-run-command t t :help "Print the file") + ("Queue" "%q" TeX-run-background nil t :help "View the printer queue" + :visible TeX-queue-command) + ("File" "%(o?)dvips %d -o %f " TeX-run-command t t + :help "Generate PostScript file") + ("Index" "makeindex %s" TeX-run-command nil t :help "Create index file") + ("Check" "lacheck %s" TeX-run-compile nil (latex-mode) + :help "Check LaTeX file for correctness") + ("Spell" "(TeX-ispell-document \"\")" TeX-run-function nil t + :help "Spell-check the document") + ("Clean" "TeX-clean" TeX-run-function nil t + :help "Delete generated intermediate files") + ("Clean All" "(TeX-clean t)" TeX-run-function nil t + :help "Delete generated intermediate and output files") + ("Other" "" TeX-run-command t t :help "Run an arbitrary command")) + "List of commands to execute on the current document. + +Each element is a list, whose first element is the name of the command +as it will be presented to the user. + +The second element is the string handed to the shell after being +expanded. The expansion is done using the information found in +`TeX-expand-list'. + +The third element is the function which actually start the process. +Several such hooks has been defined: + +TeX-run-command: Start up the process and show the output in a +separate buffer. Check that there is not two commands running for the +same file. Return the process object. + +TeX-run-format: As `TeX-run-command', but assume the output is created +by a TeX macro package. Return the process object. + +TeX-run-TeX: For TeX output. + +TeX-run-interactive: Run TeX or LaTeX interactively. + +TeX-run-BibTeX: For BibTeX output. + +TeX-run-compile: Use `compile' to run the process. + +TeX-run-shell: Use `shell-command' to run the process. + +TeX-run-discard: Start the process in the background, discarding its +output. + +TeX-run-background: Start the process in the background, show output +in other window. + +TeX-run-silent: Start the process in the background. + +TeX-run-discard-foreground: Start the process in the foreground, +discarding its output. + +TeX-run-function: Execute the Lisp function or function call +specified by the string in the second element. Consequently, +this hook does not start a process. + +TeX-run-discard-or-function: If the command is a Lisp function, +execute it as such, otherwise start the command as a process, +discarding its output. + +To create your own hook, define a function taking three arguments: The +name of the command, the command string, and the name of the file to +process. It might be useful to use `TeX-run-command' in order to +create an asynchronous process. + +If the fourth element is non-nil, the user will get a chance to +modify the expanded string. + +The fifth element indicates in which mode(s) the command should be +present in the Command menu. Use t if it should be active in any +mode. If it should only be present in some modes, specify a list with +the respective mode names. + +Any additional elements get just transferred to the respective menu entries." + :group 'TeX-command + :type '(repeat (group :value ("" "" TeX-run-command nil t) + (string :tag "Name") + (string :tag "Command") + (choice :tag "How" + :value TeX-run-command + (function-item TeX-run-command) + (function-item TeX-run-format) + (function-item TeX-run-TeX) + (function-item TeX-run-interactive) + (function-item TeX-run-BibTeX) + (function-item TeX-run-compile) + (function-item TeX-run-shell) + (function-item TeX-run-discard) + (function-item TeX-run-background) + (function-item TeX-run-silent) + (function-item TeX-run-discard-foreground) + (function-item TeX-run-function) + (function-item TeX-run-discard-or-function) + (function :tag "Other")) + (boolean :tag "Prompt") + (choice :tag "Modes" + (const :tag "All" t) + (set (const :tag "Plain TeX" plain-tex-mode) + (const :tag "LaTeX" latex-mode) + (const :tag "DocTeX" doctex-mode) + (const :tag "ConTeXt" context-mode) + (const :tag "Texinfo" texinfo-mode) + (const :tag "AmSTeX" ams-tex-mode))) + (repeat :tag "Menu elements" :inline t sexp)))) + +(defcustom TeX-command-output-list + '( +; Add the following line if you want to use htlatex (tex4ht) +; ("\\`htlatex" ("html")) + ) + "List of regexps and file extensions. + +Each element is a list, whose first element is a regular expression to +match against the name of the command that will be used to process the TeX +file. + +The second element is either a string or a list with a string as element. +If it is a string this is the default file extension that will be expected +for output files that are produced by commands that match the first +element. The real file extension will be obtained from the logging output +if possible, defaulting to the given string. +If it is a list, the element of the list will be the fixed extension used +without looking at the logging output. + +If this list does not yield an extension, the default is either \"dvi\" +or \"pdf\", depending on the setting of `TeX-PDF-mode'. +Extensions must be given without the \".\"." + + :group 'TeX-command + :type '(repeat (group (regexp :tag "Command Regexp") + (choice (string :tag "Default Extension") + (group (string :tag "Fixed Extension")))))) + +;; You may want to change the default LaTeX version for your site. +(defcustom LaTeX-version "2e" + "Default LaTeX version. Currently recognized is \"2\" and \"2e\"." + :group 'LaTeX + :type '(radio (const :format "%v\n%h" + :doc "\ +The executable `latex' is LaTeX version 2." + "2") + (const :format "%v\n%h" + :doc "\ +The executable `latex' is LaTeX version 2e." + "2e") + (string :tag "Other"))) + + +;; Use different compilation commands depending on style. +;; Only works if parsing is enabled. + +(defcustom LaTeX-command-style + ;; They have all been combined in LaTeX 2e. + '(("" "%(PDF)%(latex) %S%(PDFout)")) +"List of style options and LaTeX commands. + +If the first element (a regular expression) matches the name of one of +the style files, any occurrence of the string `%l' in a command in +`TeX-command-list' will be replaced with the second element. The first +match is used, if no match is found the `%l' is replaced with the empty +string." + :group 'TeX-command + :type '(repeat (group :value ("" "") + regexp (string :tag "Style")))) + +;; Enter the names of the printers available at your site, or nil if +;; you only have one printer. + +(defcustom TeX-printer-list + '(("Default" "%(o?)dvips -f %s | lpr" "lpq")) + "List of available printers. + +The first element of each entry is the printer name. + +The second element is the command used to print to this +printer. It defaults to the value of `TeX-print-command' when nil. + +The third element is the command used to examine the print queue for +this printer. It defaults to the value of `TeX-queue-command' similarly. + +Any occurrence of `%p' in the second or third element is expanded to +the printer name given in the first element, then ordinary expansion +is performed as specified in `TeX-expand-list'. + +If this list is empty, only `TeX-print-command' and `TeX-queue-command' +get consulted." + :group 'TeX-command + :type '(repeat (group (string :tag "Name") + (option (group :inline t + :extra-offset -4 + (choice :tag "Print" + (const :tag "default") + (string :format "%v")) + (option (choice :tag "Queue" + (const :tag "default") + (string + :format "%v")))))))) + +;; The name of the most used printer. + +(defcustom TeX-printer-default (or (getenv "PRINTER") + (and TeX-printer-list + (car (car TeX-printer-list))) + "lp") + "*Default printer to use with `TeX-command'." + :group 'TeX-command + :type 'string) + +(defcustom TeX-print-style '(("^landscape$" "-t landscape")) + "List of style options and print options. + +If the first element (a regular expression) matches the name of one of +the style files, any occurrence of the string `%r' in a command in +`TeX-command-list' will be replaced with the second element. The first +match is used, if no match is found the `%r' is replaced with the empty +string." + :group 'TeX-command + :type '(repeat (group regexp (string :tag "Command")))) + +;; This is the list of expansion for the commands in +;; TeX-command-list. Not likely to be changed, but you may e.g. want +;; to handle .ps files. + +(defcustom TeX-expand-list + '(("%p" TeX-printer-query) ;%p must be the first entry + ("%q" (lambda () + (TeX-printer-query t))) + ("%V" (lambda () + (TeX-source-correlate-start-server-maybe) + (TeX-view-command-raw))) + ("%vv" (lambda () + (TeX-source-correlate-start-server-maybe) + (TeX-output-style-check TeX-output-view-style))) + ("%v" (lambda () + (TeX-source-correlate-start-server-maybe) + (TeX-style-check TeX-view-style))) + ("%r" (lambda () + (TeX-style-check TeX-print-style))) + ("%l" (lambda () + (TeX-style-check LaTeX-command-style))) + ("%(PDF)" (lambda () + (if (and (eq TeX-engine 'default) + (or TeX-PDF-mode + TeX-DVI-via-PDFTeX)) + "pdf" + ""))) + ("%(PDFout)" (lambda () + (cond ((and (eq TeX-engine 'xetex) + (not TeX-PDF-mode)) + " -no-pdf") + ((and (eq TeX-engine 'luatex) + (not TeX-PDF-mode)) + " --output-format=dvi") + ((and (eq TeX-engine 'default) + (not TeX-PDF-mode) + TeX-DVI-via-PDFTeX) + " \"\\pdfoutput=0 \"") + (t "")))) + ("%(mode)" (lambda () + (if TeX-interactive-mode + "" + " -interaction=nonstopmode"))) + ("%(o?)" (lambda () (if (eq TeX-engine 'omega) "o" ""))) + ("%(tex)" (lambda () (eval (nth 2 (assq TeX-engine (TeX-engine-alist)))))) + ("%(latex)" (lambda () (eval (nth 3 (assq TeX-engine (TeX-engine-alist)))))) + ("%(execopts)" ConTeXt-expand-options) + ("%S" TeX-source-correlate-expand-options) + ("%dS" TeX-source-specials-view-expand-options) + ("%cS" TeX-source-specials-view-expand-client) + ("%(outpage)" (lambda () + (if TeX-source-correlate-output-page-function + (funcall TeX-source-correlate-output-page-function) + "1"))) + ;; `file' means to call `TeX-master-file' or `TeX-region-file' + ("%s" file nil t) + ("%t" file t t) + ("%`" (lambda nil + (setq TeX-command-pos t TeX-command-text ""))) + (" \"\\" (lambda nil + (if (eq TeX-command-pos t) + (setq TeX-command-pos pos + pos (+ 3 pos)) + (setq pos (1+ pos))))) + ("\"" (lambda nil (if (numberp TeX-command-pos) + (setq TeX-command-text + (concat + TeX-command-text + (substring command + TeX-command-pos + (1+ pos))) + command + (concat + (substring command + 0 + TeX-command-pos) + (substring command + (1+ pos))) + pos TeX-command-pos + TeX-command-pos t) + (setq pos (1+ pos))))) + ("%'" (lambda nil + (prog1 + (if (stringp TeX-command-text) + (progn + (setq pos (+ (length TeX-command-text) 9) + TeX-command-pos + (and (string-match " " + (funcall file t t)) + "\"")) + (concat TeX-command-text " \"\\input\"")) + (setq TeX-command-pos nil) + "") + (setq TeX-command-text nil)))) + ("%n" TeX-current-line) + ("%d" file "dvi" t) + ("%f" file "ps" t) + ("%o" (lambda nil (funcall file (TeX-output-extension) t))) + ;; for source specials the file name generated for the xdvi + ;; command needs to be relative to the master file, just in + ;; case the file is in a different subdirectory + ("%b" TeX-current-file-name-master-relative) + ;; the following is for preview-latex. + ("%m" preview-create-subdirectory)) + "List of expansion strings for TeX command names. + +Each entry is a list with two or more elements. The first element is +the string to be expanded. The second element is the name of a +function returning the expanded string when called with the remaining +elements as arguments. The special value `file' will be expanded to +the name of the file being processed, with an optional extension." + :group 'TeX-command + :type '(repeat (group (string :tag "Key") + (sexp :tag "Expander") + (repeat :inline t + :tag "Arguments" + (sexp :format "%v"))))) + + +;; The following dependencies are not done with autoload cookies since +;; they are only useful when tex.el is loaded, anyway. tex-buf.el +;; should remain unloaded as long as one is only editing files, so +;; requiring it here would be wrong. + +(autoload 'TeX-region-create "tex-buf" nil nil) +(autoload 'TeX-save-document "tex-buf" nil t) +(autoload 'TeX-home-buffer "tex-buf" nil t) +(autoload 'TeX-pin-region "tex-buf" nil t) +(autoload 'TeX-command-region "tex-buf" nil t) +(autoload 'TeX-command-buffer "tex-buf" nil t) +(autoload 'TeX-command-master "tex-buf" nil t) +(autoload 'TeX-command "tex-buf" nil nil) +(autoload 'TeX-kill-job "tex-buf" nil t) +(autoload 'TeX-recenter-output-buffer "tex-buf" nil t) +(autoload 'TeX-next-error "tex-buf" nil t) +(autoload 'TeX-region-file "tex-buf" nil nil) +(autoload 'TeX-current-offset "tex-buf" nil nil) +(autoload 'TeX-process-set-variable "tex-buf" nil nil) +(autoload 'TeX-view "tex-buf" nil t) + +;;; Portability. + +(require 'easymenu) + +(eval-and-compile + (if (featurep 'xemacs) + (defun TeX-maybe-remove-help (menu) + "Removes :help entries from menus, since XEmacs does not like them. +Also does other stuff." + (cond ((consp menu) + (cond ((eq (car menu) :help) + (TeX-maybe-remove-help (cddr menu))) + ((eq (car menu) :visible) + (cons :included + (cons (cadr menu) + (TeX-maybe-remove-help (cddr menu))))) + (t (cons (TeX-maybe-remove-help (car menu)) + (TeX-maybe-remove-help (cdr menu)))))) + ((vectorp menu) + (vconcat (TeX-maybe-remove-help (append menu nil)))) + (t menu))) + (defun TeX-maybe-remove-help (menu) + "Compatibility function that would remove :help entries if on XEmacs, +but does nothing in Emacs." + menu)) + (defmacro TeX-menu-with-help (menu) + "Compatibility macro that removes :help entries if on XEmacs. +Also does other stuff." + (TeX-maybe-remove-help menu))) + + +;;; Documentation for Info-goto-emacs-command-node and similar + +(eval-after-load 'info '(dolist (elt '("TeX" "LaTeX" "ConTeXt" "Texinfo" + "docTeX")) + (add-to-list 'Info-file-list-for-emacs + (cons elt "AUCTeX")))) + +(defadvice hack-one-local-variable (after TeX-hack-one-local-variable-after + activate) + "Call minor mode function if minor mode variable is found." + (let ((var (ad-get-arg 0)) + (val (ad-get-arg 1))) + ;; Instead of checking for each mode explicitely `minor-mode-list' + ;; could be used. But this may make the byte compiler pop up. + (when (memq var '(TeX-PDF-mode + TeX-source-correlate-mode TeX-interactive-mode + TeX-fold-mode LaTeX-math-mode)) + (if (symbol-value val) (funcall var 1) (funcall var 0))))) + +(defvar TeX-overlay-priority-step 16 + "Numerical difference of priorities between nested overlays. +The step should be big enough to allow setting a priority for new +overlays between two existing ones.") + + +;;; Special support for XEmacs + +(when (featurep 'xemacs) + + (defun TeX-read-string + (prompt &optional initial-input history default-value) + (condition-case nil + (read-string prompt initial-input history default-value t) + (wrong-number-of-arguments + (read-string prompt initial-input history default-value)))) + + (defun TeX-mark-active () + ;; In Lucid (mark) returns nil when not active. + (if zmacs-regions + (mark) + (mark t))) + + (defun TeX-active-mark () + (and zmacs-regions (mark))) + + (fset 'TeX-activate-region (symbol-function 'zmacs-activate-region)) + + ;; I am aware that this counteracts coding conventions but I am sick + ;; of it. + (unless (fboundp 'line-beginning-position) + (defalias 'line-beginning-position 'point-at-bol)) + (unless (fboundp 'line-end-position) + (defalias 'line-end-position 'point-at-eol)) + + (defun TeX-overlay-prioritize (start end) + "Calculate a priority for an overlay extending from START to END. +The calculated priority is lower than the minimum of priorities +of surrounding overlays and higher than the maximum of enclosed +overlays." + (let (inner-priority outer-priority + (prios (cons nil nil))) + (map-extents + #'(lambda (ov prios) + (and + (or (eq (extent-property ov 'category) 'TeX-fold) + (extent-property ov 'preview-state)) + (setcar prios + (max (or (car prios) 0) + (extent-property ov 'priority)))) + nil) + nil start end prios 'start-and-end-in-region 'priority) + (map-extents + #'(lambda (ov prios) + (and + (or (eq (extent-property ov 'category) 'TeX-fold) + (extent-property ov 'preview-state)) + (setcdr prios + (min (or (cdr prios) most-positive-fixnum) + (extent-property ov 'priority)))) + nil) + nil start end prios + '(start-and-end-in-region negate-in-region) 'priority) + (setq inner-priority (car prios) outer-priority (cdr prios)) + (cond ((and inner-priority (not outer-priority)) + (+ inner-priority TeX-overlay-priority-step)) + ((and (not inner-priority) outer-priority) + (/ outer-priority 2)) + ((and inner-priority outer-priority) + (+ (/ (- outer-priority inner-priority) 2) inner-priority)) + (t TeX-overlay-priority-step)))) ) + + +(if (fboundp 'completing-read-multiple) + (defalias 'TeX-completing-read-multiple 'completing-read-multiple) + (defun TeX-completing-read-multiple + (prompt table &optional predicate require-match initial-input + hist def inherit-input-method) + "Poor mans implementation of Emacs' `completing-read-multiple' for XEmacs. +The XEmacs package edit-utils-2.32 includes `crm.el'." + (multi-prompt "," nil prompt table predicate require-match initial-input + hist))) + +(if (fboundp 'line-number-at-pos) + (defalias 'TeX-line-number-at-pos 'line-number-at-pos) + ;; `line-number-at-pos' from `simple.el' in Emacs CVS (2006-06-07) + (defun TeX-line-number-at-pos (&optional pos) + "Return (narrowed) buffer line number at position POS. +If POS is nil, use current buffer location." + (let ((opoint (or pos (point))) start) + (save-excursion + (goto-char (point-min)) + (setq start (point)) + (goto-char opoint) + (forward-line 0) + (1+ (count-lines start (point))))))) + +;;; Special support for GNU Emacs + +(unless (featurep 'xemacs) + + (defun TeX-read-string (prompt &optional initial-input history default-value) + (read-string prompt initial-input history default-value t)) + + (defun TeX-mark-active () + ;; In FSF 19 mark-active indicates if mark is active. + mark-active) + + (defun TeX-active-mark () + (and transient-mark-mode mark-active)) + + (defun TeX-activate-region () + nil) + + (defun TeX-overlay-prioritize (start end) + "Calculate a priority for an overlay extending from START to END. +The calculated priority is lower than the minimum of priorities +of surrounding overlays and higher than the maximum of enclosed +overlays." + (let (outer-priority inner-priority ov-priority) + (dolist (ov (overlays-in start end)) + (when (or (eq (overlay-get ov 'category) 'TeX-fold) + (overlay-get ov 'preview-state)) + (setq ov-priority (overlay-get ov 'priority)) + (if (>= (overlay-start ov) start) + (setq inner-priority (max ov-priority (or inner-priority + ov-priority))) + (setq outer-priority (min ov-priority (or outer-priority + ov-priority)))))) + (cond ((and inner-priority (not outer-priority)) + (+ inner-priority TeX-overlay-priority-step)) + ((and (not inner-priority) outer-priority) + (/ outer-priority 2)) + ((and inner-priority outer-priority) + (+ (/ (- outer-priority inner-priority) 2) inner-priority)) + (t TeX-overlay-priority-step)))) ) + +(defun TeX-delete-dups-by-car (alist &optional keep-list) + "Return a list of all elements in ALIST, but each car only once. +Elements of KEEP-LIST are not removed even if duplicate." + ;; Copy of `reftex-uniquify-by-car' (written by David Kastrup). + (setq keep-list (sort (copy-sequence keep-list) #'string<)) + (setq alist (sort (copy-sequence alist) + (lambda (a b) + (string< (car a) (car b))))) + (let ((new alist) elt) + (while new + (setq elt (caar new)) + (while (and keep-list (string< (car keep-list) elt)) + (setq keep-list (cdr keep-list))) + (unless (and keep-list (string= elt (car keep-list))) + (while (string= elt (car (cadr new))) + (setcdr new (cddr new)))) + (setq new (cdr new)))) + alist) + +(defun TeX-delete-duplicate-strings (list) + "Return a list of all strings in LIST, but each only once." + (setq list (TeX-sort-strings list)) + (let ((new list) elt) + (while new + (setq elt (car new)) + (while (string= elt (cadr new)) + (setcdr new (cddr new))) + (setq new (cdr new)))) + list) + +(defun TeX-sort-strings (list) + "Return sorted list of all strings in LIST." + (sort (copy-sequence list) #'string<)) + +;;; Buffer + +(defgroup TeX-output nil + "Parsing TeX output." + :prefix "TeX-" + :group 'AUCTeX) + +(defcustom TeX-display-help t + "Control type of help display when stepping through errors with \\[TeX-next-error]. +If t display help buffer. If nil display message about error in +echo area. If `expert' display output buffer with raw processor output." + :group 'TeX-output + :type '(choice (const :tag "Help buffer" t) + (const :tag "Echo area" nil) + (const :tag "Output buffer" expert))) + +(defcustom TeX-debug-bad-boxes nil + "Non-nil means also find overfull/underfull box warnings with \\[TeX-next-error]." + :group 'TeX-output + :type 'boolean) + +(defcustom TeX-debug-warnings nil + "Non-nil means also find LaTeX or package warnings with \\[TeX-next-error]." + :group 'TeX-output + :type 'boolean) + +(defun TeX-toggle-debug-bad-boxes () + "Toggle if the debugger should display \"bad boxes\" too." + (interactive) + (setq TeX-debug-bad-boxes (not TeX-debug-bad-boxes)) + (message (concat "TeX-debug-bad-boxes: " + (if TeX-debug-bad-boxes "on" "off")))) + +(defun TeX-toggle-debug-warnings () + "Toggle if the debugger should display warnings too." + (interactive) + (setq TeX-debug-warnings (not TeX-debug-warnings)) + (message (concat "TeX-debug-warnings: " + (if TeX-debug-warnings "on" "off")))) + +;;; Mode names. + +(defvar TeX-base-mode-name nil + "Base name of mode.") +(make-variable-buffer-local 'TeX-base-mode-name) + +(defun TeX-set-mode-name (&optional changed local reset) + "Build and set the mode name. +The base mode name will be concatenated with indicators for +helper modes where appropriate. + +If CHANGED is non-nil, it indicates which global mode +may have changed so that all corresponding buffers +without a local value might get their name updated. +A value of t will thus update all buffer names. + +If LOCAL is non-nil and CHANGED is buffer-local, only +a local change has been performed and only the local +name is to be updated. + +If RESET is non-nil, `TeX-command-next' is reset to +`TeX-command-default' in updated buffers." + (if (and changed + (not (and local (local-variable-p changed (current-buffer))))) + (dolist (buffer (buffer-list)) + (and (local-variable-p 'TeX-mode-p buffer) + (not (local-variable-p changed buffer)) + (with-current-buffer buffer (TeX-set-mode-name nil nil reset)))) + (if TeX-mode-p + (let ((trailing-flags + (concat + (and (boundp 'TeX-fold-mode) TeX-fold-mode "F") + (and (boundp 'LaTeX-math-mode) LaTeX-math-mode "M") + (and TeX-PDF-mode "P") + (and TeX-interactive-mode "I") + (and TeX-source-correlate-mode "S")))) + (setq mode-name (concat TeX-base-mode-name + (when (> (length trailing-flags) 0) + (concat "/" trailing-flags)))) + (when reset + (TeX-process-set-variable (TeX-master-file) + 'TeX-command-next TeX-command-default) + (TeX-process-set-variable (TeX-region-file) + 'TeX-command-next TeX-command-default)) + (set-buffer-modified-p (buffer-modified-p)))))) + +(defun TeX-mode-prefix () + "Return the prefix of the current mode as string." + (cdr (assoc major-mode '((plain-tex-mode . "plain-TeX") + (latex-mode . "LaTeX") + (doctex-mode . "docTeX") + (texinfo-mode . "Texinfo") + (context-mode . "ConTeXt"))))) + +;;; Viewing + +(defgroup TeX-view nil + "Calling viewers from AUCTeX." + :group 'TeX-command) + +(defcustom TeX-view-style + `((,(concat + "^" (regexp-opt '("a4paper" "a4dutch" "a4wide" "sem-a4")) "$") + "%(o?)xdvi %dS -paper a4 %d") + (,(concat "^" (regexp-opt '("a5paper" "a5comb")) "$") + "%(o?)xdvi %dS -paper a5 %d") + ("^b5paper$" "%(o?)xdvi %dS -paper b5 %d") + ("^letterpaper$" "%(o?)xdvi %dS -paper us %d") + ("^legalpaper$" "%(o?)xdvi %dS -paper legal %d") + ("^executivepaper$" "%(o?)xdvi %dS -paper 7.25x10.5in %d") + ("^landscape$" "%(o?)xdvi %dS -paper a4r -s 0 %d") + ;; The latest xdvi can show embedded postscript. If you don't + ;; have that, uncomment next line. + ;; ("^epsf$" "ghostview %f") + ("." "%(o?)xdvi %dS %d")) + "List of style options and view options. + +If the first element (a regular expression) matches the name of +one of the style files, any occurrence of the string `%v' in a +command in `TeX-command-list' will be replaced with the second +element. The first match is used, if no match is found the `%v' +is replaced with the empty string. + +As a default, the \"View\" command in `TeX-command-list' is set +to `%V'. This means that `TeX-output-view-style' will be +consulted before `TeX-view-style'. Only if no match is found in +`TeX-output-view-style' the settings in `TeX-view-style' will be +considered. If you want to bypass `TeX-output-view-style', which +is not recommended because it is more powerful than +`TeX-view-style', use `%v' in the \"View\" command." + :group 'TeX-view + :type '(repeat (group regexp (string :tag "Command")))) + +(defcustom TeX-output-view-style + `(("^dvi$" ("^landscape$" "^pstricks$\\|^pst-\\|^psfrag$") + "%(o?)dvips -t landscape %d -o && gv %f") + ("^dvi$" "^pstricks$\\|^pst-\\|^psfrag$" "%(o?)dvips %d -o && gv %f") + ("^dvi$" (,(concat + "^" (regexp-opt '("a4paper" "a4dutch" "a4wide" "sem-a4")) "$") + "^landscape$") + "%(o?)xdvi %dS -paper a4r -s 0 %d") + ("^dvi$" ,(concat + "^" (regexp-opt '("a4paper" "a4dutch" "a4wide" "sem-a4")) "$") + "%(o?)xdvi %dS -paper a4 %d") + ("^dvi$" (,(concat "^" (regexp-opt '("a5paper" "a5comb")) "$") + "^landscape$") + "%(o?)xdvi %dS -paper a5r -s 0 %d") + ("^dvi$" ,(concat "^" (regexp-opt '("a5paper" "a5comb")) "$") + "%(o?)xdvi %dS -paper a5 %d") + ("^dvi$" "^b5paper$" "%(o?)xdvi %dS -paper b5 %d") + ("^dvi$" "^letterpaper$" "%(o?)xdvi %dS -paper us %d") + ("^dvi$" "^legalpaper$" "%(o?)xdvi %dS -paper legal %d") + ("^dvi$" "^executivepaper$" "%(o?)xdvi %dS -paper 7.25x10.5in %d") + ("^dvi$" "." "%(o?)xdvi %dS %d") + ("^pdf$" "." "xpdf -remote %s -raise %o %(outpage)") + ("^html?$" "." "netscape %o")) + "List of output file extensions and view options. + +If the first element (a regular expression) matches the output +file extension, and the second element (a regular expression) +matches the name of one of the style options, any occurrence of +the string `%V' in a command in `TeX-command-list' will be +replaced with the third element. The first match is used; if no +match is found the `%V' is replaced with `%v'. The outcome of `%v' +is determined by the settings in `TeX-view-style' which therefore +serves as a fallback for `TeX-output-view-style'. The second +element may also be a list of regular expressions, in which case +all the regular expressions must match for the element to apply." + :group 'TeX-view + :type '(repeat (group + (regexp :tag "Extension") + (choice regexp (repeat :tag "List" regexp)) + (string :tag "Command")))) + +;;; Viewing (new implementation) + +(defvar TeX-view-predicate-list-builtin + '((output-dvi + (string-match "dvi" (TeX-output-extension))) + (output-pdf + (string-match "pdf" (TeX-output-extension))) + (output-html + (string-match "html" (TeX-output-extension))) + (style-pstricks + (TeX-match-style "^pstricks$\\|^pst-\\|^psfrag$")) + (engine-omega + (eq TeX-engine 'omega)) + (engine-xetex + (eq TeX-engine 'xetex)) + (mode-io-correlate + TeX-source-correlate-mode) + (paper-landscape + (TeX-match-style "\\`landscape\\'")) + (paper-portrait + (not (TeX-match-style "\\`landscape\\'"))) + (paper-a4 + (TeX-match-style "\\`a4paper\\|a4dutch\\|a4wide\\|sem-a4\\'")) + (paper-a5 + (TeX-match-style "\\`a5paper\\|a5comb\\'")) + (paper-b5 + (TeX-match-style "\\`b5paper\\'")) + (paper-letter + (TeX-match-style "\\`letterpaper\\'")) + (paper-legal + (TeX-match-style "\\`legalpaper\\'")) + (paper-executive + (TeX-match-style "\\`executivepaper\\'"))) + "Alist of built-in predicates for viewer selection and invocation. +See the doc string of `TeX-view-predicate-list' for a short +description of each predicate.") + +(defcustom TeX-view-predicate-list nil + "Alist of predicates for viewer selection and invocation. +The key of each list item is a symbol and the value a Lisp form +to be evaluated. The form should return nil if the predicate is +not fulfilled. + +Built-in predicates provided in `TeX-view-predicate-list-builtin' +can be overwritten by defining predicates with the same symbol. + +The following built-in predicates are available: + `output-dvi': The output is a DVI file. + `output-pdf': The output is a PDF file. + `output-html': The output is an HTML file. + `style-pstricks': The document loads a PSTricks package. + `engine-omega': The Omega engine is used for typesetting. + `engine-xetex': The XeTeX engine is used for typesetting. + `mode-io-correlate': TeX Source Correlate mode is active. + `paper-landscape': The document is typeset in landscape orientation. + `paper-portrait': The document is not typeset in landscape orientation. + `paper-a4': The paper format is A4. + `paper-a5': The paper format is A5. + `paper-b5': The paper format is B5. + `paper-letter': The paper format is letter. + `paper-legal': The paper format is legal. + `paper-executive': The paper format is executive." + :group 'TeX-view + :type '(alist :key-type symbol :value-type (group sexp))) + +(defvar TeX-view-program-list-builtin + (cond + ((eq system-type 'windows-nt) + '(("Yap" ("yap -1" (mode-io-correlate " -s %n%b") " %o")) + ("dvips and start" "dvips %d -o && start \"\" %f") + ("start" "start \"\" %o"))) +;; XXX: We need the advice of a Mac OS X user to configure this +;; correctly and test it. +;; ((eq system-type 'darwin) +;; '(("Preview.app" "open -a Preview.app %o") +;; ("Skim" "open -a Skim.app %o") +;; ("displayline" "displayline %n %o %b") +;; ("open" "open %o"))) + (t + '(("xdvi" ("%(o?)xdvi" + (mode-io-correlate " -sourceposition \"%n %b\" -editor \"%cS\"") + ((paper-a4 paper-portrait) " -paper a4") + ((paper-a4 paper-landscape) " -paper a4r") + ((paper-a5 paper-portrait) " -paper a5") + ((paper-a5 paper-landscape) " -paper a5r") + (paper-b5 " -paper b5") + (paper-letter " -paper us") + (paper-legal " -paper legal") + (paper-executive " -paper 7.25x10.5in") + " %d")) + ("dvips and gv" "%(o?)dvips %d -o && gv %f") + ("gv" "gv %o") + ("xpdf" ("xpdf -remote %s -raise %o" (mode-io-correlate " %(outpage)"))) + ("Evince" ("evince" (mode-io-correlate " -p %(outpage)") " %o")) + ("xdg-open" "xdg-open %o")))) + "Alist of built-in viewer specifications. +This variable should not be changed by the user who can use +`TeX-view-program-list' to add new viewers or overwrite the +definition of built-in ones. The latter variable also contains a +description of the data format.") + +(defcustom TeX-view-program-list nil + "Alist of viewer specifications. +This variable can be used to specify how a viewer is to be +invoked and thereby add new viewers on top of the built-in list +of viewers defined in `TeX-view-program-list-builtin' or override +entries in the latter. + +The car of each item is a string with a user-readable name. The +second element can be a command line to be run as a process or a +Lisp function to be executed. The command line can either be +specified as a single string or a list of strings and two-part +lists. The first element of the two-part lists is a symbol or a +list of symbols referring to one or more of the predicates in +`TeX-view-predicate-list' or `TeX-view-predicate-list-builtin'. +The second part of the two-part lists is a command line part. +The command line for the viewer is constructed by concatenating +the command line parts. Parts with a predicate are only +considered if the predicate was evaluated with a positive result. +Note that the command line can contain placeholders as defined in +`TeX-expand-list' which are expanded before the viewer is called. + +The use of a function as the second element only works if the +View command in `TeX-command-list' makes use of the hook +`TeX-run-discard-or-function'. + +Note: Predicates defined in the current Emacs session will only +show up in the customization interface for this variable after +restarting Emacs." + :group 'TeX-view + :type + `(alist + :key-type (string :tag "Name") + :value-type + (choice + (group :tag "Command" (string :tag "Command")) + (group :tag "Command parts" + (repeat + :tag "Command parts" + (choice + (string :tag "Command part") + (list :tag "Predicate and command part" + ,(let (list) + ;; Build the list of available predicates. + (mapc (lambda (spec) + (add-to-list 'list `(const ,(car spec)))) + (append TeX-view-predicate-list + TeX-view-predicate-list-builtin)) + ;; Sort the list alphabetically. + (setq list (sort list + (lambda (a b) + (string< + (downcase (symbol-name (cadr a))) + (downcase (symbol-name (cadr b))))))) + `(choice + (choice :tag "Predicate" ,@list) + (repeat :tag "List of predicates" + (choice :tag "Predicate" ,@list)))) + (string :tag "Command part"))))) + (group :tag "Function" function)))) + +;; XXX: Regarding a possibility to (manually) run an update command, +;; one could support this through `TeX-view' by letting it temporarily +;; set a variable which is checked with a predicate in the viewer +;; selection. If the check is positive, the update command is run +;; instead of the normal viewer command. Direct support through the +;; View command would require a predicate which knows when an update +;; has to be done. +(defcustom TeX-view-program-selection + (cond + ((eq system-type 'windows-nt) + '(((output-dvi style-pstricks) "dvips and start") + (output-dvi "Yap") + (output-pdf "start") + (output-html "start"))) +;; XXX: We need the advice of a Mac OS X user to configure this +;; correctly and test it. +;; ((eq system-type 'darwin) +;; '((output-dvi "open") +;; (output-pdf "open") +;; (output-html "open"))) + (t + '(((output-dvi style-pstricks) "dvips and gv") + (output-dvi "xdvi") + (output-pdf "Evince") + (output-html "xdg-open")))) + "Alist of predicates and viewers. +Each entry consists of a list with two elements. The first +element is a symbol or list of symbols referring to predicates as +defined in `TeX-view-predicate-list' or +`TeX-view-predicate-list-builtin'. The second element is a +string referring to the name of a viewer as defined in +`TeX-view-program-list' or `TeX-view-program-list-builtin'. + +When a viewer is called for, the entries are evaluated in turn +and the viewer related to the first entry all predicates of which +are evaluated positively is chosen." + :group 'TeX-view + :type `(alist :key-type + ;; Offer list of defined predicates. + ,(let (list) + (mapc (lambda (spec) + (add-to-list 'list `(const ,(car spec)))) + (append TeX-view-predicate-list + TeX-view-predicate-list-builtin)) + (setq list (sort list + (lambda (a b) + (string< + (downcase (symbol-name (cadr a))) + (downcase (symbol-name (cadr b))))))) + `(choice (choice :tag "Single predicate" ,@list) + (repeat :tag "Multiple predicates" + (choice ,@list)))) + :value-type + ;; Offer list of defined viewers. + (group (choice :tag "Viewer" + ,@(let (list) + (mapc (lambda (spec) + (add-to-list 'list + `(const ,(car spec)))) + (append TeX-view-program-list + TeX-view-program-list-builtin)) + (sort list + (lambda (a b) + (string< (downcase (cadr a)) + (downcase (cadr b)))))))))) + +(defun TeX-match-style (regexp) + "Check if a style matching REGEXP is active." + (TeX-member regexp (TeX-style-list) 'string-match)) + +(defun TeX-view-match-predicate (predicate) + "Check if PREDICATE is true. +PREDICATE can be a symbol or a list of symbols defined in +`TeX-view-predicate-list-builtin' or `TeX-view-predicate-list'. +In case of a single symbol, return t if the predicate is true, +nil otherwise. In case of a list of symbols, return t if all +predicates are true, nil otherwise." + (let ((pred-symbols (if (listp predicate) predicate (list predicate))) + (pred-defs (append TeX-view-predicate-list + TeX-view-predicate-list-builtin)) + (result t) + elt) + (while (and (setq elt (pop pred-symbols)) result) + (unless (eval (cadr (assq elt pred-defs))) + (setq result nil))) + result)) + +(defun TeX-view-command-raw () + "Choose a viewer and return its unexpanded command string." + (let ((selection TeX-view-program-selection) + entry viewer spec command) + ;; Find the appropriate viewer. + (while (and (setq entry (pop selection)) (not viewer)) + (when (TeX-view-match-predicate (car entry)) + (setq viewer (cadr entry)))) + (unless viewer + (error "No matching viewer found")) + ;; Get the command line or function spec. + (setq spec (cadr (assoc viewer (append TeX-view-program-list + TeX-view-program-list-builtin)))) + (cond ((functionp spec) + ;; Converting the function call to a string is ugly, but + ;; the backend currently only supports strings. + (prin1-to-string spec)) + ((stringp spec) + spec) + (t + ;; Build the unexpanded command line. Pieces with predicates are + ;; only added if the predicate is evaluated positively. + (dolist (elt spec) + (cond ((stringp elt) + (setq command (concat command elt))) + ((listp elt) + (when (TeX-view-match-predicate (car elt)) + (setq command (concat command (cadr elt))))))) + command)))) + +;;; Engine + +(defvar TeX-engine-alist-builtin + '((default "Default" TeX-command LaTeX-command ConTeXt-engine) + (xetex "XeTeX" "xetex" "xelatex" "xetex") + (luatex "LuaTeX" "luatex" "lualatex" "luatex") + (omega "Omega" TeX-Omega-command LaTeX-Omega-command ConTeXt-Omega-engine)) + "Alist of built-in TeX engines and associated commands. +For a description of the format see `TeX-engine-alist'.") + +(defcustom TeX-engine-alist nil + "Alist of TeX engines and associated commands. +Each entry is a list with a maximum of five elements. The first +element is a symbol used to identify the engine. The second is a +string describing the engine. The third is the command to be +used for plain TeX. The fourth is the command to be used for +LaTeX. The fifth is the command to be used for the --engine +parameter of ConTeXt's texexec program. Each command can either +be a variable or a string. An empty string or nil means there is +no command available. + +You can override a built-in engine defined in the variable +`TeX-engine-alist-builtin' by adding an entry beginning with the +same symbol as the built-in entry to `TeX-engine-alist'." + :group 'TeX-command + :type '(repeat (group symbol + (string :tag "Name") + (choice :tag "Plain TeX command" string variable) + (choice :tag "LaTeX command" string variable) + (choice :tag "ConTeXt command" string variable)))) + +(defun TeX-engine-alist () + "Return an alist of TeX engines. +The function appends the built-in engine specs from +`TeX-engine-alist-builtin' and the user-defined engines from +`TeX-engine-alist' and deletes any entries from the built-in part +where an entry with the same car exists in the user-defined part." + (TeX-delete-dups-by-car (append TeX-engine-alist TeX-engine-alist-builtin))) + +(defcustom TeX-engine 'default + (concat "Type of TeX engine to use. +It should be one of the following symbols:\n\n" + (mapconcat (lambda (x) (format "* `%s'" (car x))) + (TeX-engine-alist) "\n")) + :group 'TeX-command + :type `(choice ,@(mapcar (lambda (x) + `(const :tag ,(nth 1 x) ,(car x))) + (TeX-engine-alist)))) +(make-variable-buffer-local 'TeX-engine) +(put 'TeX-engine 'safe-local-variable + (lambda (arg) (memq arg (mapcar 'car TeX-engine-alist-builtin)))) + +(defun TeX-engine-set (type) + (concat "Set TeX engine to TYPE. +TYPE can be one of the following symbols:\n" + (mapconcat (lambda (x) (format "* `%s'" (car x))) + (TeX-engine-alist) "\n")) + (interactive (list (completing-read "Engine: " + (mapcar (lambda (x) + (symbol-name (car x))) + (TeX-engine-alist)) + nil t))) + (when (stringp type) + (setq type (intern type))) + (setq TeX-engine type) + ;; Automatically enable or disable TeX PDF mode as a convenience + (cond ((eq type 'xetex) (TeX-PDF-mode 1)) + ((eq type 'omega) (TeX-PDF-mode 0)))) + +(define-minor-mode TeX-Omega-mode + "Minor mode for using the Omega engine." + nil nil nil + :group 'TeX-command + (TeX-engine-set (if TeX-Omega-mode 'omega 'default))) +(defalias 'tex-omega-mode 'TeX-Omega-mode) +(make-obsolete 'TeX-Omega-mode 'TeX-engine-set) +(make-obsolete-variable 'TeX-Omega-mode 'TeX-engine) + +;;; Forward and inverse search + +(defcustom TeX-source-correlate-method 'auto + "Method to use for enabling forward and inverse search. +This can be `source-specials' if source specials should be used, +`synctex' if SyncTeX should be used, or`auto' if AUCTeX should +decide. + +Setting this variable does not take effect if TeX Source +Correlate mode has already been active. Restart Emacs in this +case." + :type '(choice (const auto) (const synctex) (const source-specials)) + :group 'TeX-view) + +(defvar TeX-source-correlate-method-active nil + "Method actually used for forward and inverse search.") + +(defvar TeX-source-correlate-output-page-function nil + "Symbol of function returning an output page relating to buffer position. +The function should take no arguments and return the page numer +as a string.") +(make-variable-buffer-local 'TeX-source-correlate-output-page-function) + +(defcustom TeX-source-correlate-start-server 'ask + "Control if server should be started for inverse search." + :type '(choice (const :tag "Always" t) + (const :tag "Never" nil) + (const :tag "Ask" ask)) + :group 'TeX-view) +(when (fboundp 'defvaralias) + (defvaralias 'TeX-source-specials-view-start-server + 'TeX-source-correlate-start-server)) + +(defvar TeX-source-correlate-start-server-asked nil + "Keep track if question about server start search was asked.") + +(defvar TeX-source-correlate-start-server-flag nil + "If non-nil, `TeX-source-correlate-start-server-maybe' will start a server. +Code related to features requiring a server, e.g. for inverse +search, can set the variable.") + +(defun TeX-source-correlate-gnuserv-p () + "Guess whether to use gnuserv when a server is requested." + (cond ((and (boundp 'gnuserv-process) + (processp gnuserv-process))) + ((and (boundp 'server-process) + (processp server-process)) + nil) + ((featurep 'xemacs)))) + +(defun TeX-source-correlate-server-enabled-p () + "Return non-nil if Emacs server or gnuserv is enabled." + (let* ((gnuserv-p (TeX-source-correlate-gnuserv-p)) + (process (if gnuserv-p 'gnuserv-process 'server-process))) + (and (boundp process) (processp (symbol-value process))))) + +(defun TeX-source-correlate-start-server-maybe () + "Start Emacs server or gnuserv if a feature using it is enabled. +This is the case if `TeX-source-correlate-start-server-flag' is non-nil." + (when (and TeX-source-correlate-start-server-flag + (not (TeX-source-correlate-server-enabled-p))) + (let* ((gnuserv-p (TeX-source-correlate-gnuserv-p)) + (start (if gnuserv-p 'gnuserv-start 'server-start))) + (cond + ;; Server should be started unconditionally + ((eq TeX-source-correlate-start-server t) + (funcall start)) + ;; Ask user if server is to be started + ((and (eq TeX-source-correlate-start-server 'ask) + (not TeX-source-correlate-start-server-asked) + (prog1 + (y-or-n-p (format "Start %s for inverse search in viewer? " + (if gnuserv-p + "gnuserv" + "Emacs server"))) + (setq TeX-source-correlate-start-server-asked t))) + (funcall start)))))) + +(defun TeX-source-correlate-determine-method () + "Determine which method is available for forward and inverse search." + (let ((help (condition-case nil + (with-output-to-string + (call-process LaTeX-command + nil (list standard-output nil) nil "--help")) + (error "")))) + (if (string-match "^[ ]*-synctex" help) + 'synctex + 'source-specials))) + +(defun TeX-source-correlate-expand-options () + "Return TeX engine command line option for forward search facilities. +The return value depends on the value of `TeX-source-correlate-mode'. +If this is nil, an empty string will be returned." + (if TeX-source-correlate-mode + (if (eq TeX-source-correlate-method-active 'source-specials) + (concat TeX-source-specials-tex-flags + (if TeX-source-specials-places + ;; -src-specials=WHERE: insert source specials + ;; in certain places of the DVI file. WHERE is a + ;; comma-separated value list: cr display hbox + ;; math par parend vbox + (concat "=" (mapconcat 'identity + TeX-source-specials-places ",")))) + TeX-synctex-tex-flags) + "")) + +(defvar TeX-source-correlate-map + (let ((map (make-sparse-keymap))) + ;; (if (featurep 'xemacs) + ;; (define-key map [(control button1)] #'TeX-view-mouse) + ;; (define-key map [C-down-mouse-1] #'TeX-view-mouse)) + map) + "Keymap for `TeX-source-correlate-mode'. +You could use this for unusual mouse bindings.") + +(define-minor-mode TeX-source-correlate-mode + "Minor mode for forward and inverse search. + +If enabled, the viewer can be advised to show the output page +corresponding to the point in the source and vice versa. + +The method to be used can be controlled with the variable +`TeX-source-correlate-method'. Currently source specials or +SyncTeX are recognized." + :group 'TeX-view + ;; Since this is a global minor mode and we don't want to require + ;; tex.el when the mode variable is set, the mode function is called + ;; explicitely (if necessary) in `VirTeX-common-initialization'. We + ;; do it there because otherwise `kill-all-local-variables' would + ;; reset `TeX-source-correlate-output-page-function' which is + ;; buffer-local. + :global t + (set-keymap-parent TeX-mode-map (and TeX-source-correlate-mode + TeX-source-correlate-map)) + (TeX-set-mode-name 'TeX-source-correlate-mode t t) + (setq TeX-source-correlate-start-server-flag TeX-source-correlate-mode) + (unless TeX-source-correlate-method-active + (setq TeX-source-correlate-method-active + (if (eq TeX-source-correlate-method 'auto) + (TeX-source-correlate-determine-method) + TeX-source-correlate-method))) + (when (eq TeX-source-correlate-method-active 'synctex) + (setq TeX-source-correlate-output-page-function + (when TeX-source-correlate-mode + 'TeX-synctex-output-page)))) +(defalias 'TeX-source-specials-mode 'TeX-source-correlate-mode) +(make-obsolete 'TeX-source-specials-mode 'TeX-source-correlate-mode) +(defalias 'tex-source-correlate-mode 'TeX-source-correlate-mode) +(put 'TeX-source-correlate-mode 'safe-local-variable 'TeX-booleanp) +;; We do not want the custom variable to require tex.el. This is only +;; necessary if AUCTeX was compiled with Emacs 21. +(put 'TeX-source-correlate-mode 'custom-requests nil) +(setq minor-mode-map-alist + (delq (assq 'TeX-source-correlate-mode minor-mode-map-alist) + minor-mode-map-alist)) + + +;;; Source Specials + +(defcustom TeX-source-specials-tex-flags "-src-specials" + "Extra flags to pass to TeX commands to generate source specials." + :group 'TeX-view + :type 'string) + +(defcustom TeX-source-specials-places nil + "List of places where to insert source specials into the DVI file. +If nil, use (La)TeX's defaults." + :group 'TeX-view + :type '(list (set :inline t + ;; :tag "Options known to work" + ;; cr display hbox math par parend vbox + (const "cr") + (const "display") + (const "hbox") + (const "math") + (const "par") + (const "parend") + (const "vbox")) + (repeat :inline t + :tag "Other options" + (string)))) + +(defcustom TeX-source-specials-view-position-flags + "-sourceposition \"%n %b\"" + "Flags to pass to the DVI viewer commands for the position in the source." + :group 'TeX-view + :type 'string) + +(defcustom TeX-source-specials-view-editor-flags + "-editor \"%cS\"" + "Flags to pass to DVI viewer commands for inverse search." + :group 'TeX-view + :type 'string) + +(defcustom TeX-source-specials-view-gnuclient-flags + "-q +%%l %%f" + "Flags to pass to gnuclient for inverse search." + :group 'TeX-view + :type 'string) + +(defcustom TeX-source-specials-view-emacsclient-flags + "--no-wait +%%l %%f" + "Flags to emacsclient for inverse search." + :group 'TeX-view + :type 'string) + +;; FIXME: Make client binaries configurable. +(defun TeX-source-specials-view-expand-client () + "Return gnuclient or emacslient executable with options. +Return the full path to the executable if possible." + (let* ((gnuserv-p (TeX-source-correlate-gnuserv-p)) + (client-base (if gnuserv-p + "gnuclient" + "emacsclient")) + (client-full (and invocation-directory + (expand-file-name client-base + invocation-directory))) + (options (if gnuserv-p + TeX-source-specials-view-gnuclient-flags + TeX-source-specials-view-emacsclient-flags))) + (if (and client-full (file-executable-p client-full)) + (concat client-full " " options) + (concat client-base " " options)))) + +(defun TeX-source-specials-view-expand-options (&optional viewer) + "Return source specials command line option for viewer command. +The return value depends on the values of +`TeX-source-correlate-mode' and +`TeX-source-correlate-method-active'. If those are nil or not +`source-specials' respectively, an empty string will be +returned." + (if (and TeX-source-correlate-mode + (eq TeX-source-correlate-method-active 'source-specials)) + (concat TeX-source-specials-view-position-flags + (when (TeX-source-correlate-server-enabled-p) + (concat " " TeX-source-specials-view-editor-flags))) + "")) + +;;; SyncTeX + +(defvar TeX-synctex-tex-flags "--synctex=1" + "Extra flags to pass to TeX commands to enable SyncTeX.") + +(defun TeX-synctex-output-page () + "Return the page corresponding to the current source position. +This method assumes that the document was compiled with SyncTeX +enabled and the `synctex' binary is available." + (let ((synctex-output + (with-output-to-string + (call-process "synctex" nil (list standard-output nil) nil "view" + "-i" (format "%s:%s:%s" (line-number-at-pos) + (current-column) + ;; The file name relative to the + ;; directory of the master file. + (file-relative-name + (buffer-file-name) + (file-name-directory + (TeX-active-master)))) + "-o" (TeX-active-master (TeX-output-extension)))))) + (when (string-match "Page:\\([0-9]+\\)" synctex-output) + (match-string 1 synctex-output)))) + +;;; Miscellaneous minor modes + +(defvar TeX-mode-p nil + "This indicates a TeX mode being active.") +(make-variable-buffer-local 'TeX-mode-p) + +(defun TeX-mode-set (var value) + (set-default var value) + (TeX-set-mode-name var nil t)) + +(defcustom TeX-PDF-mode nil nil + :group 'TeX-command + :set 'TeX-mode-set + :type 'boolean) +(put 'TeX-PDF-mode 'safe-local-variable 'TeX-booleanp) + +(define-minor-mode TeX-PDF-mode + "Minor mode for using PDFTeX. + +If enabled, PDFTeX will be used as an executable by default. +You can customize an initial value, and you can use the +function `TeX-global-PDF-mode' for toggling this value." + :group 'TeX-command + (when (eq TeX-engine 'omega) + (setq TeX-PDF-mode nil)) + (setq TeX-PDF-mode-parsed nil) + (TeX-set-mode-name nil nil t) + (setq TeX-output-extension + (if TeX-PDF-mode "pdf" "dvi"))) +(add-to-list 'minor-mode-alist '(TeX-PDF-mode "")) + +(defun TeX-global-PDF-mode (&optional arg) + "Toggle default for `TeX-PDF-mode'." + (interactive "P") + (prog1 + (setq-default TeX-PDF-mode + (if arg (> (prefix-numeric-value arg) 0) + (not (default-value 'TeX-PDF-mode)))) + (TeX-set-mode-name 'TeX-PDF-mode nil t))) + +(defalias 'tex-pdf-mode 'TeX-PDF-mode) + +(defvar TeX-PDF-mode-parsed nil + "Set if `TeX-PDF-mode' has come about by parsing.") + +(make-variable-buffer-local 'TeX-PDF-mode-parsed) + +(defun TeX-PDF-mode-parsed (arg) + "Change `TeX-PDF-mode' to ARG based on parsing. +If this conflicts with previous parsed settings, +just use the default. If an explicit setting is +already established, don't do anything." + +;; Basically we have the following situations: +;; TeX-PDF-mode-parsed (local-variable-p 'TeX-PDF-mode): +;; nil nil : virgin state +;; nil t : stably set state (possibly because of conflicting parse info) +;; t t : non-conflicting parsed info + + (if TeX-PDF-mode-parsed + (unless (eq TeX-PDF-mode arg) + (TeX-PDF-mode (if (default-value 'TeX-PDF-mode) 1 0))) + (unless (local-variable-p 'TeX-PDF-mode (current-buffer)) + (TeX-PDF-mode (if arg 1 0)) + (setq TeX-PDF-mode-parsed t)))) + +(defun TeX-PDF-mode-on () + "Use only from parsing routines." + (TeX-PDF-mode-parsed t)) + +(defun TeX-PDF-mode-off () + "Use only from parsing routines." + (TeX-PDF-mode-parsed nil)) + +(defcustom TeX-DVI-via-PDFTeX nil + "Whether to use PDFTeX also for producing DVI files." + :group 'TeX-command + :type 'boolean) + +(define-minor-mode TeX-interactive-mode + "Minor mode for interactive runs of TeX." + nil nil nil + :group 'TeX-command + (TeX-set-mode-name 'TeX-interactive-mode t t)) +(defalias 'tex-interactive-mode 'TeX-interactive-mode) +(add-to-list 'minor-mode-alist '(TeX-interactive-mode "")) + +;;; Commands + +(defgroup TeX-command-name nil + "Names for external commands in AUCTeX." + :group 'TeX-command) + +(defcustom TeX-command-BibTeX "BibTeX" + "*The name of the BibTeX entry in `TeX-command-list'." + :group 'TeX-command-name + :type 'string) + (make-variable-buffer-local 'TeX-command-BibTeX) + +(defcustom TeX-command-Show "View" + "*The default command to show (view or print) a TeX file. +Must be the car of an entry in `TeX-command-list'." + :group 'TeX-command-name + :type 'string) + (make-variable-buffer-local 'TeX-command-Show) + +(defcustom TeX-command-Print "Print" + "The name of the Print entry in `TeX-command-Print'." + :group 'TeX-command-name + :type 'string) + +(defcustom TeX-command-Queue "Queue" + "The name of the Queue entry in `TeX-command-Queue'." + :group 'TeX-command-name + :type 'string) + +(defvar TeX-trailer-start nil + "Regular expression delimiting start of trailer in a TeX file.") + + (make-variable-buffer-local 'TeX-trailer-start) + +(defvar TeX-header-end nil + "Regular expression delimiting end of header in a TeX file.") + + (make-variable-buffer-local 'TeX-header-end) + +(defvar TeX-command-default nil + "The default command for `TeX-command' in the current major mode.") + + (make-variable-buffer-local 'TeX-command-default) + +(put 'TeX-command-default 'safe-local-variable 'stringp) + +(defvar TeX-clean-default-intermediate-suffixes + '("\\.aux" "\\.bbl" "\\.blg" "\\.brf" "\\.fot" + "\\.glo" "\\.gls" "\\.idx" "\\.ilg" "\\.ind" + "\\.lof" "\\.log" "\\.lot" "\\.nav" "\\.out" + "\\.snm" "\\.toc" "\\.url" "\\.synctex\\.gz") + "List of regexps matching suffixes of files to be cleaned. +Used as a default in TeX, LaTeX and docTeX mode.") + +(defvar TeX-clean-default-output-suffixes + '("\\.dvi" "\\.pdf" "\\.ps" "\\.xdv") + "List of regexps matching suffixes of files to be cleaned. +Used as a default in TeX, LaTeX and docTeX mode.") + +(defcustom TeX-clean-confirm t + "If non-nil, ask before deleting files." + :type 'boolean + :group 'TeX-command) + +(autoload 'dired-mark-pop-up "dired") + +(defun TeX-clean (&optional arg) + "Delete generated files associated with current master and region files. +If prefix ARG is non-nil, not only remove intermediate but also +output files." + (interactive "P") + (let* ((mode-prefix (TeX-mode-prefix)) + (suffixes (append (symbol-value + (intern (concat mode-prefix + "-clean-intermediate-suffixes"))) + (when arg + (symbol-value + (intern (concat mode-prefix + "-clean-output-suffixes")))))) + (master (TeX-active-master)) + (master-dir (file-name-directory master)) + (regexp (concat "\\(" + (file-name-nondirectory master) "\\|" + (TeX-region-file nil t) + "\\)" + "\\(" + (mapconcat 'identity suffixes "\\|") + "\\)\\'" + "\\|" (TeX-region-file t t))) + (files (when regexp + (directory-files (or master-dir ".") nil regexp)))) + (if files + (when (or (not TeX-clean-confirm) + (condition-case nil + (dired-mark-pop-up " *Deletions*" 'delete + (if (> (length files) 1) + files + (cons t files)) + 'y-or-n-p "Delete files? ") + (wrong-type-argument ; e.g. with Emacs 21 + (y-or-n-p (format "Delete %S? " (car files)))))) + (dolist (file files) + (delete-file (concat master-dir file)))) + (message "No files to be deleted")))) + + +;;; Master File + +(defcustom TeX-master t + "*The master file associated with the current buffer. +If the file being edited is actually included from another file, you +can tell AUCTeX the name of the master file by setting this variable. +If there are multiple levels of nesting, specify the top level file. + +If this variable is nil, AUCTeX will query you for the name. + +If the variable is t, AUCTeX will assume the file is a master file +itself. + +If the variable is 'shared, AUCTeX will query for the name, but not +change the file. + +If the variable is 'dwim, AUCTeX will try to avoid querying by +attempting to `do what I mean'; and then change the file. + +It is suggested that you use the File Variables (see the info node in +the Emacs manual) to set this variable permanently for each file." + :group 'TeX-command + :group 'TeX-parse + :type '(choice (const :tag "Query" nil) + (const :tag "This file" t) + (const :tag "Shared" shared) + (const :tag "Dwim" dwim) + (string :format "%v"))) +(make-variable-buffer-local 'TeX-master) +(put 'TeX-master 'safe-local-variable + '(lambda (x) + (or (stringp x) + (member x (quote (t nil shared dwim)))))) + +(defcustom TeX-one-master "\\.\\(texi?\\|dtx\\)$" + "*Regular expression matching ordinary TeX files. + +You should set this variable to match the name of all files, where +automatically adding a file variable with the name of the master file +is a good idea. When AUCTeX adds the name of the master file as a +file variable, it does not need to ask next time you edit the file. + +If you dislike AUCTeX automatically modifying your files, you can set +this variable to \"\"." + :group 'TeX-command + :type 'regexp) + +(defvar TeX-convert-master t + "*If not nil, automatically convert ``Master:'' lines to file variables. +This will be done when AUCTeX first try to use the master file.") + +;; Can be let-bound temporarily in order to inhibit the master file question +;; by using its value instead in case `TeX-master' is nil or 'shared. +(defvar TeX-transient-master nil) + +(defun TeX-dwim-master () + "Find a likely `TeX-master'." + (let ((dir default-directory)) + (dolist (buf (buffer-list)) + (when (with-current-buffer buf + (and (equal dir default-directory) + (stringp TeX-master))) + (return (with-current-buffer buf TeX-master)))))) + +(defun TeX-master-file-ask () + "Ask for master file, set `TeX-master' and add local variables." + (interactive) + (if (TeX-local-master-p) + (error "Master file already set") + (let* ((default (TeX-dwim-master)) + (name (or (and (eq 'dwim TeX-master) default) + (condition-case nil + (read-file-name (format "Master file: (default %s) " + (or default "this file")) + nil default) + (quit ""))))) + (cond ((string= name "") + (setq TeX-master t)) + ((string= name default) + (setq TeX-master default) + (TeX-add-local-master)) + ((or + ;; Default `read-file-name' proposes and buffer visits a file. + (string= (expand-file-name name) (buffer-file-name)) + ;; Default of `read-file-name' and buffer does not visit a file. + (string= name default-directory) + ;; User typed in an empty minibuffer. + (string= name "")) + (setq TeX-master t) + (TeX-add-local-master)) + (t + (setq TeX-master (TeX-strip-extension (file-relative-name name) + (list TeX-default-extension) + 'path)) + (TeX-add-local-master)))))) + +(defun TeX-master-file (&optional extension nondirectory ask) + "Set and return the name of the master file for the current document. + +If optional argument EXTENSION is non-nil, add that file extension to +the name. Special value t means use `TeX-default-extension'. + +If optional second argument NONDIRECTORY is non-nil, do not include +the directory. + +If optional third argument ASK is non-nil, ask the user for the +name of master file if it cannot be determined otherwise. + +Currently it will check for the presence of a ``Master:'' line in +the beginning of the file, but that feature will be phased out." + (interactive) + (if (eq extension t) + (setq extension TeX-default-extension)) + (let ((my-name (if (buffer-file-name) + (TeX-strip-extension nil (list TeX-default-extension) t) + ""))) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (cond + ((and TeX-transient-master + (or (not TeX-master) (eq TeX-master 'shared))) + (setq TeX-master TeX-transient-master)) + ;; Special value 't means it is own master (a free file). + ((equal TeX-master my-name) + (setq TeX-master t)) + + ;; For files shared between many documents. + ((and (eq 'shared TeX-master) ask) + (setq TeX-master + (let* ((default (TeX-dwim-master)) + (name (read-file-name + (format "Master file: (default %s) " + (or default "this file")) + nil default))) + (cond ((string= name default) + default) + ((or + ;; Default `read-file-name' proposes and + ;; buffer visits a file. + (string= (expand-file-name name) + (buffer-file-name)) + ;; Default of `read-file-name' and + ;; buffer does not visit a file. + (string= name default-directory) + ;; User typed in an empty minibuffer. + (string= name "")) + t) + (t + (TeX-strip-extension + name (list TeX-default-extension) 'path)))))) + + ;; We might already know the name. + ((or (eq TeX-master t) (stringp TeX-master)) TeX-master) + + ;; Support the ``Master:'' line (under protest!) + ((re-search-forward + "^%% *[Mm]aster:?[ \t]*\\([^ \t\n]+\\)" 500 t) + (setq TeX-master + (TeX-strip-extension (TeX-match-buffer 1) + (list TeX-default-extension))) + (if TeX-convert-master + (progn + (beginning-of-line) + (kill-line 1) + (TeX-add-local-master)))) + + ;; Ask the user (but add it as a local variable). + (ask (TeX-master-file-ask))))) + + (let ((name (if (stringp TeX-master) + TeX-master + my-name))) + + (if (TeX-match-extension name) + ;; If it already has an extension... + (if (equal extension TeX-default-extension) + ;; Use instead of the default extension + (setq extension nil) + ;; Otherwise drop it. + (setq name (TeX-strip-extension name)))) + + ;; Remove directory if needed. + (if nondirectory + (setq name (file-name-nondirectory name))) + + (if extension + (concat name "." extension) + name)))) + +(defun TeX-master-directory () + "Directory of master file." + (file-name-as-directory + (abbreviate-file-name + (substitute-in-file-name + (expand-file-name + (let ((dir (file-name-directory (TeX-master-file)))) + (if dir (directory-file-name dir) ".")) + (and buffer-file-name + (file-name-directory buffer-file-name))))))) + +(defun TeX-add-local-master () + "Add local variable for `TeX-master'." + (when (and (buffer-file-name) + (string-match TeX-one-master + (file-name-nondirectory (buffer-file-name))) + (not buffer-read-only)) + (goto-char (point-max)) + (if (re-search-backward (concat "^\\([^\n]+\\)Local " "Variables:") + (- (point-max) 3000) t) + (let ((prefix (TeX-match-buffer 1))) + (re-search-forward (regexp-quote (concat prefix + "End:"))) + (beginning-of-line 1) + (insert prefix "TeX-master: " (prin1-to-string TeX-master) "\n")) + (let ((comment-prefix (cond ((eq major-mode 'texinfo-mode) "@c ") + ((eq major-mode 'doctex-mode) "% ") + (t "%%% "))) + (mode (concat (and (boundp 'japanese-TeX-mode) japanese-TeX-mode + "japanese-") + (substring (symbol-name major-mode) 0 -5)))) + (newline) + (when (eq major-mode 'doctex-mode) + (insert comment-prefix TeX-esc "endinput\n")) + (insert + comment-prefix "Local " "Variables: \n" + comment-prefix "mode: " mode "\n" + comment-prefix "TeX-master: " (prin1-to-string TeX-master) "\n" + comment-prefix "End: \n"))))) + +(defun TeX-local-master-p () + "Return non-nil if there is a `TeX-master' entry in local variables spec. +Return nil otherwise." + (save-excursion + ;; XXX: Checking -*- line necessary as well? + (goto-char (point-max)) + (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move) + (re-search-forward "^%+ *TeX-master:" nil t))) + +;;; Style Paths + +(defcustom TeX-style-global (expand-file-name "style" TeX-data-directory) + "*Directory containing hand generated TeX information. + +These correspond to TeX macros shared by all users of a site." + :group 'TeX-file + :type 'directory) + +(defcustom TeX-auto-local "auto" + "*Directory containing automatically generated TeX information. + +This correspond to TeX macros found in the current directory, and must +be relative to that." + :group 'TeX-file + :type 'string) + +(defcustom TeX-style-local "style" + "*Directory containing hand generated TeX information. + +These correspond to TeX macros found in the current directory, and must +be relative to that." + :group 'TeX-file + :type 'string) + +(defun TeX-split-string (regexp string) + "Return a list of strings. +Given REGEXP the STRING is split into sections which in string was +seperated by REGEXP. + +Examples: + + (TeX-split-string \"\:\" \"abc:def:ghi\") + -> (\"abc\" \"def\" \"ghi\") + + (TeX-split-string \" +\" \"dvips -Plw -p3 -c4 testfile.dvi\") + + -> (\"dvips\" \"-Plw\" \"-p3\" \"-c4\" \"testfile.dvi\") + +If REGEXP is nil, or \"\", an error will occur." + + (let ((start 0) result match) + (while (setq match (string-match regexp string start)) + (push (substring string start match) result) + (setq start (match-end 0))) + (push (substring string start) result) + (nreverse result))) + +(defun TeX-parse-path (env) + "Return a list if private TeX directories found in environment variable ENV." + (let* ((value (getenv env)) + (entries (and value + (TeX-split-string + (if (string-match ";" value) ";" ":") + value))) + entry + answers) + (while entries + (setq entry (car entries)) + (setq entries (cdr entries)) + (setq entry (file-name-as-directory + (if (string-match "/?/?\\'" entry) + (substring entry 0 (match-beginning 0)) + entry))) + (or (not (file-name-absolute-p entry)) + (member entry (append '("/" "\\") TeX-macro-global)) + (setq answers (cons entry answers)))) + answers)) + +(defun TeX-macro-global () + "Return directories containing the site's TeX macro and style files." + (or (TeX-tree-expand '("$SYSTEXMF" "$TEXMFLOCAL" "$TEXMFMAIN" "$TEXMFDIST") + "latex" '("/tex/" "/bibtex/bst/")) + '("/usr/share/texmf/tex/" "/usr/share/texmf/bibtex/bst/"))) + +(defun TeX-macro-private () + "Return directories containing the user's TeX macro and style files." + (TeX-tree-expand '("$TEXMFHOME") "latex" '("/tex/" "/bibtex/bst/"))) + +(defun TeX-tree-expand (trees program subdirs) + "Return directories corresponding to the TeX trees TREES. +This is done calling `kpsewhich' where PROGRAM is passed as the +parameter for --progname. SUBDIRS are subdirectories which are +appended to the directories of the TeX trees." + (let (path-list path exit-status input-dir-list) + (condition-case nil + (catch 'success + (dotimes (i (safe-length trees)) + (setq path (with-output-to-string + (setq exit-status + (call-process + "kpsewhich" nil + (list standard-output nil) nil + "--progname" program + "--expand-braces" (nth i trees))))) + (if (zerop exit-status) + (progn (add-to-list 'path-list path) + (when (zerop i) (throw 'success nil))) + (setq path (with-output-to-string + (setq exit-status + (call-process + "kpsewhich" nil + (list standard-output nil) nil + "--progname" program + "--expand-path" (nth i trees))))) + (when (zerop exit-status) (add-to-list 'path-list path))))) + (error nil)) + (dolist (elt path-list) + (let ((separators (if (string-match "^[A-Za-z]:" elt) + "[\n\r;]" + "[\n\r:]"))) + (dolist (item (condition-case nil + (split-string elt separators t) + ;; COMPATIBILITY for XEmacs <= 21.4.15 + (error (delete "" (split-string elt separators))))) + (when (string-match "^!+" item) + (setq item (substring item (match-end 0) (length item)))) + (when (string-match "/+$" item) + (setq item (substring item 0 (match-beginning 0)))) + (dolist (subdir subdirs) + (when (file-exists-p (file-name-as-directory (concat item subdir))) + (add-to-list 'input-dir-list (concat item subdir))))))) + input-dir-list)) + +(defcustom TeX-macro-global (TeX-macro-global) + "Directories containing the site's TeX macro and style files." + :group 'TeX-file + :type '(repeat (directory :format "%v"))) + +(defcustom TeX-macro-private (or (append (TeX-parse-path "TEXINPUTS") + (TeX-parse-path "BIBINPUTS")) + (TeX-macro-private)) + "Directories where you store your personal TeX macros." + :group 'TeX-file + :type '(repeat (file :format "%v"))) + +(defcustom TeX-auto-private + (list (expand-file-name TeX-auto-local + (or (and (boundp 'user-emacs-directory) + (concat user-emacs-directory "auctex/")) + "~/.emacs.d/auctex/"))) + "List of directories containing automatically generated AUCTeX style files. + +These correspond to the personal TeX macros." + :group 'TeX-file + :type '(repeat (file :format "%v"))) + +(if (stringp TeX-auto-private) ;Backward compatibility + (setq TeX-auto-private (list TeX-auto-private))) + +(defcustom TeX-style-private + (list (expand-file-name TeX-style-local + (or (and (boundp 'user-emacs-directory) + (concat user-emacs-directory "auctex/")) + "~/.emacs.d/auctex/"))) + "List of directories containing hand-generated AUCTeX style files. + +These correspond to the personal TeX macros." + :group 'TeX-file + :type '(repeat (file :format "%v"))) + +(if (stringp TeX-style-private) ;Backward compatibility + (setq TeX-style-private (list TeX-style-private))) + +(defcustom TeX-style-path + (let ((path)) + ;; Put directories in an order where the more local files can + ;; override the more global ones. + (mapcar (lambda (file) (when file (add-to-list 'path file t))) + (append (list TeX-auto-global TeX-style-global) + TeX-auto-private TeX-style-private + (list TeX-auto-local TeX-style-local))) + path) + "List of directories to search for AUCTeX style files. +Per default the list is built from the values of the variables +`TeX-auto-global', `TeX-style-global', `TeX-auto-private', +`TeX-style-private', `TeX-auto-local', and `TeX-style-local'." + :group 'TeX-file + :type '(repeat (file :format "%v"))) + +(defcustom TeX-check-path + (append (list ".") TeX-macro-private TeX-macro-global) + "Directory path to search for dependencies. + +If nil, just check the current file. +Used when checking if any files have changed." + :group 'TeX-file + :type '(repeat (file :format "%v"))) + +;;; Style Files + +(defvar TeX-style-hook-list nil + "List of TeX style hooks currently loaded. + +Each entry is a list where the first element is the name of the style, +and the remaining elements are hooks to be run when that style is +active.") + +(defcustom TeX-byte-compile nil + "*Not nil means try to byte compile auto files before loading." + :group 'TeX-parse + :type 'boolean) + +(defun TeX-load-style (style) + "Search for and load each definition for STYLE in `TeX-style-path'." + (cond ((assoc style TeX-style-hook-list)) ; We already found it + ((string-match "\\`\\(.+[/\\]\\)\\([^/\\]*\\)\\'" style) ;Complex path + (let* ((dir (substring style (match-beginning 1) (match-end 1))) + (style (substring style (match-beginning 2) (match-end 2))) + (master-dir (if (stringp TeX-master) + (file-name-directory + (file-relative-name TeX-master)) + "./")) + (TeX-style-path (append (list (expand-file-name + TeX-auto-local dir) + (expand-file-name + TeX-auto-local master-dir) + (expand-file-name + TeX-style-local dir) + (expand-file-name + TeX-style-local master-dir)) + TeX-style-path))) + (TeX-load-style style))) + (t ;Relative path + ;; Insert empty list to mark the fact that we have searched. + (setq TeX-style-hook-list (cons (list style) TeX-style-hook-list)) + ;; Now check each element of the path + (dolist (name TeX-style-path) + (TeX-load-style-file (expand-file-name style name)))))) + +(defun TeX-load-style-file (file) + "Load FILE checking for a Lisp extensions." + (let ((el (concat file ".el")) + (elc (concat file ".elc"))) + (cond ((file-newer-than-file-p el elc) + (if (file-readable-p el) + (if (and TeX-byte-compile + (file-writable-p elc) + (save-excursion + ;; `byte-compile-file' switches buffer in Emacs 20.3. + (byte-compile-file el)) + (file-readable-p elc)) + (load-file elc) + (load-file el)))) + ((file-readable-p elc) + (load-file elc)) + ((file-readable-p el) + (load-file el))))) + +(defun TeX-add-style-hook (style hook) + "Give STYLE yet another HOOK to run." + (let ((entry (assoc style TeX-style-hook-list))) + (cond ((null entry) + ;; New style, add entry. + (setq TeX-style-hook-list (cons (list style hook) + TeX-style-hook-list))) + ((member hook entry) + ;; Old style, hook already there, do nothing. + nil) + (t + ;; Old style, new hook. + (setcdr entry (cons hook (cdr entry))))))) + +(defun TeX-unload-style (style) + "Forget that we once loaded STYLE." + (cond ((null (assoc style TeX-style-hook-list))) + ((equal (car (car TeX-style-hook-list)) style) + (setq TeX-style-hook-list (cdr TeX-style-hook-list))) + (t + (let ((entry TeX-style-hook-list)) + (while (not (equal (car (car (cdr entry))) style)) + (setq entry (cdr entry))) + (setcdr entry (cdr (cdr entry))))))) + +(defcustom TeX-virgin-style (if (and TeX-auto-global + (file-directory-p TeX-auto-global)) + "virtex" + "NoVirtexSymbols") + "Style all documents use." + :group 'TeX-parse + :type 'string) + +(defvar TeX-active-styles nil + "List of styles currently active in the document.") + (make-variable-buffer-local 'TeX-active-styles) + +(defun TeX-run-style-hooks (&rest styles) + "Run the TeX style hooks STYLES." + (mapcar (lambda (style) + ;; Avoid recursion. + (unless (TeX-member style TeX-active-styles 'string-equal) + (setq TeX-active-styles + (cons style TeX-active-styles)) + (TeX-load-style style) + (let ((default-directory default-directory)) + ;; Complex path. + (when (string-match "\\`\\(.+[/\\]\\)\\([^/\\]*\\)\\'" style) + ;; Set `default-directory' to directory of master + ;; file since style files not stored in the fixed + ;; style directories are usually located there. + (setq default-directory (save-match-data + (TeX-master-directory)) + style (substring style + (match-beginning 2) (match-end 2)))) + (mapcar 'funcall + (cdr-safe (assoc style TeX-style-hook-list)))))) + styles)) + +(defcustom TeX-parse-self nil + "Parse file after loading it if no style hook is found for it." + :group 'TeX-parse + :type 'boolean) + +(defvar TeX-style-hook-applied-p nil + "Nil, unless the style specific hooks have been applied.") + (make-variable-buffer-local 'TeX-style-hook-applied-p) + +(defvar TeX-update-style-hook nil + "Hook run as soon as style specific hooks were applied.") + +(defun TeX-update-style (&optional force) + "Run style specific hooks for the current document. + +Only do this if it has not been done before, or if optional argument +FORCE is not nil." + (unless (or (and (boundp 'TeX-auto-update) + (eq TeX-auto-update 'BibTeX)) ; Not a real TeX buffer + (and (not force) + TeX-style-hook-applied-p)) + (setq TeX-style-hook-applied-p t) + (message "Applying style hooks...") + (TeX-run-style-hooks (TeX-strip-extension nil nil t)) + ;; Run parent style hooks if it has a single parent that isn't itself. + (if (or (not (memq TeX-master '(nil t))) + (and (buffer-file-name) + (string-match TeX-one-master + (file-name-nondirectory (buffer-file-name))))) + (TeX-run-style-hooks (TeX-master-file))) + (if (and TeX-parse-self + (null (cdr-safe (assoc (TeX-strip-extension nil nil t) + TeX-style-hook-list)))) + (TeX-auto-apply)) + (run-hooks 'TeX-update-style-hook) + (message "Applying style hooks... done"))) + +(defvar TeX-remove-style-hook nil + "List of hooks to call when we remove the style specific information.") + (make-variable-buffer-local 'TeX-remove-style-hook) + +(defun TeX-remove-style () + "Remove all style specific information." + (setq TeX-style-hook-applied-p nil) + (run-hooks 'TeX-remove-style-hook) + (setq TeX-active-styles (list TeX-virgin-style))) + +(defun TeX-style-list () + "Return a list of all styles (subfiles) used by the current document." + (TeX-update-style) + TeX-active-styles) + +;;; Special Characters + +(defvar TeX-esc "\\" "The TeX escape character.") + (make-variable-buffer-local 'TeX-esc) + +(defvar TeX-grop "{" "The TeX group opening character.") + (make-variable-buffer-local 'TeX-grop) + +(defvar TeX-grcl "}" "The TeX group closing character.") + (make-variable-buffer-local 'TeX-grcl) + +(defcustom plain-TeX-enable-toolbar t + "Enable TeX tool bar in plain TeX mode." + :group 'TeX-tool-bar + :type 'boolean) + +(defun plain-TeX-maybe-install-toolbar () + "Conditionally install tool bar buttons for plain TeX mode. +Install tool bar if `plain-TeX-enable-toolbar' is non-nil." + (when plain-TeX-enable-toolbar + ;; Defined in `tex-bar.el': + (TeX-install-toolbar))) + +;;; Symbols + +;; Must be before keymaps. + +(defgroup TeX-macro nil + "Support for TeX macros in AUCTeX." + :prefix "TeX-" + :group 'AUCTeX) + +(defcustom TeX-complete-word 'ispell-complete-word + "*Function to call for completing non-macros in `tex-mode'." + :group 'TeX-macro) + +(defvar TeX-complete-list nil + "List of ways to complete the preceding text. + +Each entry is a list with the following elements: + +0. Regexp matching the preceding text. +1. A number indicating the subgroup in the regexp containing the text. +2. A function returning an alist of possible completions. +3. Text to append after a succesful completion. + +Or alternatively: + +0. Regexp matching the preceding text. +1. Function to do the actual completion.") + +(defun TeX-complete-symbol () + "Perform completion on TeX/LaTeX symbol preceding point." + (interactive "*") + (let ((list TeX-complete-list) + entry) + (while list + (setq entry (car list) + list (cdr list)) + (if (TeX-looking-at-backward (car entry) 250) + (setq list nil))) + (if (numberp (nth 1 entry)) + (let* ((sub (nth 1 entry)) + (close (nth 3 entry)) + (begin (match-beginning sub)) + (end (match-end sub)) + (pattern (TeX-match-buffer 0)) + (symbol (buffer-substring begin end)) + (list (funcall (nth 2 entry))) + (completion (try-completion symbol list))) + (cond ((eq completion t) + (and close + (not (looking-at (regexp-quote close))) + (insert close))) + ((null completion) + (error "Can't find completion for \"%s\"" pattern)) + ((not (string-equal symbol completion)) + (delete-region begin end) + (insert completion) + (and close + (eq (try-completion completion list) t) + (not (looking-at (regexp-quote close))) + (insert close))) + (t + (message "Making completion list...") + (let ((list (all-completions symbol list nil))) + (with-output-to-temp-buffer "*Completions*" + (display-completion-list list))) + (message "Making completion list...done")))) + (funcall (nth 1 entry))))) + +(defcustom TeX-default-macro "ref" + "*The default macro when creating new ones with `TeX-insert-macro'." + :group 'TeX-macro + :type 'string) + +(make-variable-buffer-local 'TeX-default-macro) + +(defcustom TeX-insert-braces t + "*If non-nil, append a empty pair of braces after inserting a macro." + :group 'TeX-macro + :type 'boolean) + +(defcustom TeX-insert-macro-default-style 'show-optional-args + "Specifies whether `TeX-insert-macro' will ask for all optional arguments. + +If set to the symbol `show-optional-args', `TeX-insert-macro' asks for +optional arguments of TeX marcos. If set to `mandatory-args-only', +`TeX-insert-macro' asks only for mandatory argument. + +When `TeX-insert-macro' is called with \\[universal-argument], it's the other +way round. + +Note that for some macros, there are special mechanisms, see e.g. +`LaTeX-includegraphics-options-alist'." + :group 'TeX-macro + :type '(choice (const mandatory-args-only) + (const show-optional-args))) + +(defvar TeX-arg-opening-brace nil + "String used as an opening brace for argument insertion. +The variable will be temporarily let-bound with the necessary value.") + +(defvar TeX-arg-closing-brace nil + "String used as a closing brace for argument insertion. +The variable will be temporarily let-bound with the necessary value.") + +(defvar TeX-after-insert-macro-hook nil + "A hook run after `TeX-insert-macro'.") + +(defvar TeX-macro-history nil) + +(defun TeX-insert-macro (symbol) + "Insert TeX macro SYMBOL with completion. + +AUCTeX knows of some macros and may query for extra arguments, depending on +the value of `TeX-insert-macro-default-style' and whether `TeX-insert-macro' +is called with \\[universal-argument]." + ;; When called with a prefix (C-u), only ask for mandatory arguments, + ;; i.e. all optional arguments are skipped. See `TeX-parse-arguments' for + ;; details. Note that this behavior may be changed in favor of a more + ;; flexible solution in the future, therefore we don't document it at the + ;; moment. + (interactive (list (completing-read (concat "Macro (default " + TeX-default-macro + "): " + TeX-esc) + (TeX-symbol-list) nil nil nil + 'TeX-macro-history))) + (cond ((string-equal symbol "") + (setq symbol TeX-default-macro)) + ((interactive-p) + (setq TeX-default-macro symbol))) + (TeX-parse-macro symbol (cdr-safe (assoc symbol (TeX-symbol-list)))) + (run-hooks 'TeX-after-insert-macro-hook)) + +(defvar TeX-electric-macro-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map minibuffer-local-completion-map) + (define-key map " " 'minibuffer-complete-and-exit) + map)) + +(defun TeX-electric-macro () + "Insert TeX macro with completion. + +AUCTeX knows of some macros, and may query for extra arguments. +Space will complete and exit." + (interactive) + (cond ((eq (preceding-char) ?\\) + (call-interactively 'self-insert-command)) + ((eq (preceding-char) ?.) + (let ((TeX-default-macro " ") + (minibuffer-local-completion-map TeX-electric-macro-map)) + (call-interactively 'TeX-insert-macro))) + (t + (let ((minibuffer-local-completion-map TeX-electric-macro-map)) + (call-interactively 'TeX-insert-macro))))) + +(defun TeX-parse-macro (symbol args) + "How to parse TeX macros which takes one or more arguments. + +First argument SYMBOL is the name of the macro. + +If called with no additional arguments, insert macro with point +inside braces. Otherwise, each argument of this function should +match an argument to the TeX macro. What is done depend on the +type of ARGS: + + string: Use the string as a prompt to prompt for the argument. + + number: Insert that many braces, leave point inside the first. + + nil: Insert empty braces. + + t: Insert empty braces, leave point between the braces. + + other symbols: Call the symbol as a function. You can define + your own hook, or use one of the predefined argument hooks. If + you add new hooks, you can assume that point is placed directly + after the previous argument, or after the macro name if this is + the first argument. Please leave point located after the + argument you are inserting. If you want point to be located + somewhere else after all hooks have been processed, set the value + of `exit-mark'. It will point nowhere, until the argument hook + set it. By convention, these hooks all start with `TeX-arg-'. + + list: If the car is a string, insert it as a prompt and the next + element as initial input. Otherwise, call the car of the list + with the remaining elements as arguments. + + vector: Optional argument. If it has more than one element, + parse it as a list, otherwise parse the only element as above. + Use square brackets instead of curly braces, and is not inserted + on empty user input." + + (if (and (TeX-active-mark) + (> (point) (mark))) + (exchange-point-and-mark)) + (insert TeX-esc symbol) + (let ((exit-mark (make-marker)) + (position (point))) + (TeX-parse-arguments args) + (cond ((marker-position exit-mark) + (goto-char (marker-position exit-mark)) + (set-marker exit-mark nil)) + ((and TeX-insert-braces + ;; Do not add braces for macros defined as `("foo" 0)' + (not (and (= (safe-length args) 1) + (numberp (car args)) + (= (car args) 0))) + (equal position (point)) + (string-match "[a-zA-Z]+" symbol) + (not (texmathp))) + (insert TeX-grop) + (if (TeX-active-mark) + (progn + (exchange-point-and-mark) + (insert TeX-grcl)) + (insert TeX-grcl) + (backward-char)))))) + +(defun TeX-arg-string (optional &optional prompt initial-input) + "Prompt for a string. + +If OPTIONAL is not nil then the PROMPT will start with ``(Optional) ''. +INITIAL-INPUT is a string to insert before reading input." + (TeX-argument-insert + (if (and (not optional) (TeX-active-mark)) + (let ((TeX-argument (buffer-substring (point) (mark)))) + (delete-region (point) (mark)) + TeX-argument) + (read-string (TeX-argument-prompt optional prompt "Text") initial-input)) + optional)) + +(defun TeX-parse-arguments (args) + "Parse TeX macro arguments ARGS. + +See `TeX-parse-macro' for details." + (let ((last-optional-rejected nil) + skip-opt) + ;; Maybe get rid of all optional arguments. See `TeX-insert-macro' for + ;; more comments. See `TeX-insert-macro-default-style'. + (when (or (and (eq TeX-insert-macro-default-style 'show-optional-args) + (equal current-prefix-arg '(4))) + (and (eq TeX-insert-macro-default-style 'mandatory-args-only) + (null (equal current-prefix-arg '(4))))) + (while (vectorp (car args)) + (setq args (cdr args)))) + + (while args + (if (vectorp (car args)) + (unless last-optional-rejected + (let ((TeX-arg-opening-brace LaTeX-optop) + (TeX-arg-closing-brace LaTeX-optcl)) + (TeX-parse-argument t (if (equal (length (car args)) 1) + (aref (car args) 0) + (append (car args) nil))))) + (let ((TeX-arg-opening-brace TeX-grop) + (TeX-arg-closing-brace TeX-grcl)) + (setq last-optional-rejected nil) + (TeX-parse-argument nil (car args)))) + (setq args (cdr args))))) + +(defun TeX-parse-argument (optional arg) + "Depending on OPTIONAL, insert TeX macro argument ARG. +If OPTIONAL is set, only insert if there is anything to insert, and +then use square brackets instead of curly braces. + +See `TeX-parse-macro' for details." + (let (insert-flag) + (cond ((stringp arg) + (TeX-arg-string optional arg) + (setq insert-flag t)) + ((numberp arg) + (unless (< arg 1) + (TeX-parse-argument optional t) + (while (> arg 1) + (TeX-parse-argument optional nil) + (setq arg (- arg 1))))) + ((null arg) + (insert TeX-arg-opening-brace) + (when (and (not optional) (TeX-active-mark)) + (exchange-point-and-mark)) + (insert TeX-arg-closing-brace) + (setq insert-flag t)) + ((eq arg t) + (insert TeX-arg-opening-brace) + (if (and (not optional) (TeX-active-mark)) + (progn + (exchange-point-and-mark)) + (set-marker exit-mark (point))) + (insert TeX-arg-closing-brace) + (setq insert-flag t)) + ((symbolp arg) + (funcall arg optional)) + ((listp arg) + (let ((head (car arg)) + (tail (cdr arg))) + (cond ((stringp head) + (apply 'TeX-arg-string optional arg)) + ((symbolp head) + (apply head optional tail)) + (t (error "Unknown list argument type %s" + (prin1-to-string head)))))) + (t (error "Unknown argument type %s" (prin1-to-string arg)))) + (when (and insert-flag (not optional) (TeX-active-mark)) + (TeX-deactivate-mark)))) + +(defun TeX-argument-insert (name optional &optional prefix) + "Insert NAME surrounded by curly braces. + +If OPTIONAL, only insert it if not empty, and then use square brackets. +If PREFIX is given, insert it before NAME." + (if (and optional (string-equal name "")) + (setq last-optional-rejected t) + (insert TeX-arg-opening-brace) + (if prefix + (insert prefix)) + (if (and (string-equal name "") + (null (marker-position exit-mark))) + (set-marker exit-mark (point)) + (insert name)) + (insert TeX-arg-closing-brace))) + +(defun TeX-argument-prompt (optional prompt default &optional complete) + "Return a argument prompt. + +If OPTIONAL is not nil then the prompt will start with ``(Optional) ''. + +PROMPT will be used if not nil, otherwise use DEFAULT. + +Unless optional argument COMPLETE is non-nil, ``: '' will be appended." + (concat (if optional "(Optional) " "") + (if prompt prompt default) + (if complete "" ": "))) + +(defun TeX-string-divide-number-unit (string) + "Divide number and unit in STRING. +Return the number as car and unit as cdr." + (if (string-match "[0-9]*\\.?[0-9]+" string) + (list (substring string 0 (string-match "[^.0-9]" string)) + (substring string (if (string-match "[^.0-9]" string) + (string-match "[^.0-9]" string) + (length string)))) + (list "" string))) + +(defcustom TeX-default-unit-for-image "cm" + "Default unit when prompting for an image size." + :group 'TeX-macro + :type '(choice (const "cm") + (const "in") + (const "\\linewidth") + (string :tag "Other"))) + +(defun TeX-arg-maybe (symbol list form) + "Evaluates FORM, if SYMBOL is an element of LIST." + (when (memq symbol list) + (eval form))) + +(defun TeX-arg-free (optional &rest args) + "Parse its arguments but use no braces when they are inserted." + (let ((TeX-arg-opening-brace "") + (TeX-arg-closing-brace "")) + (if (equal (length args) 1) + (TeX-parse-argument optional (car args)) + (TeX-parse-argument optional args)))) + +(defun TeX-arg-literal (optional &rest args) + "Insert its arguments ARGS into the buffer. +Used for specifying extra syntax for a macro." + ;; FIXME: What is the purpose of OPTIONAL here? -- rs + (apply 'insert args)) + + +;;; Font Locking + +(defcustom TeX-install-font-lock 'font-latex-setup + "Function to call to install font lock support. +Choose `ignore' if you don't want AUCTeX to install support for font locking." + :group 'TeX-misc + :type '(radio (function-item font-latex-setup) + (function-item tex-font-setup) + (function-item ignore) + (function :tag "Other"))) + +;;; The Mode + +(defvar TeX-format-list + '(("JLATEX" japanese-latex-mode + "\\\\\\(documentstyle\\|documentclass\\)[^%\n]*{\\(j[s-]?\\|t\\)\ +\\(article\\|report\\|book\\|slides\\)") + ("JTEX" japanese-plain-tex-mode + "-- string likely in Japanese TeX --") + ("AMSTEX" ams-tex-mode + "\\\\document\\b") + ("CONTEXT" context-mode + "\\\\\\(start\\(text\\|tekst\\|proje[ck]t\\|proiect\\|\ +produ[ck]t\\|produs\\|environment\\|omgeving\\|umgebung\\|prostredi\\|mediu\\|\ +component\\|onderdeel\\|komponent[ea]\\|componenta\\)\ +\\|inizia\\(testo\\|progetto\\|prodotto\\|ambiente\\|componente\\)\ +\\)\\|%.*?interface=") + ("LATEX" latex-mode + "\\\\\\(begin\\|\\(?:sub\\)\\{0,2\\}section\\|chapter\\|documentstyle\\|\ +documentclass\\)\\b") + ("TEX" plain-tex-mode ".")) + "*List of format packages to consider when choosing a TeX mode. + +A list with an entry for each format package available at the site. + +Each entry is a list with three elements. + +1. The name of the format package. +2. The name of the major mode. +3. A regexp typically matched in the beginning of the file. + +When entering `tex-mode', each regexp is tried in turn in order to find +the major mode to be used.") + +(defcustom TeX-default-mode 'latex-mode + "*Mode to enter for a new file when it can't be determined otherwise." + :group 'TeX-misc + :type '(radio (function-item latex-mode) + (function-item plain-tex-mode) + (function :tag "Other"))) + +(defcustom TeX-force-default-mode nil + "*If set to nil, try to infer the mode of the file from its content." + :group 'TeX-misc + :type 'boolean) + +;;;###autoload +(defun TeX-tex-mode () + "Major mode in AUCTeX for editing TeX or LaTeX files. +Tries to guess whether this file is for plain TeX or LaTeX. + +The algorithm is as follows: + + 1) if the file is empty or `TeX-force-default-mode' is not set to nil, + `TeX-default-mode' is chosen + 2) If \\documentstyle or \\begin{, \\section{, \\part{ or \\chapter{ is + found, `latex-mode' is selected. + 3) Otherwise, use `plain-tex-mode'" + (interactive) + + (funcall (if (or (equal (buffer-size) 0) + TeX-force-default-mode) + TeX-default-mode + (save-excursion + (goto-char (point-min)) + (let ((comment-start-skip ;Used by TeX-in-comment + (concat + "\\(\\(^\\|[^\\\n]\\)\\(" + (regexp-quote TeX-esc) + (regexp-quote TeX-esc) + "\\)*\\)\\(%+ *\\)")) + (entry TeX-format-list) + answer) + (while (and entry (not answer)) + (if (re-search-forward (nth 2 (car entry)) + 10000 t) + (if (not (TeX-in-comment)) + (setq answer (nth 1 (car entry)))) + (setq entry (cdr entry)))) + (if answer + answer + TeX-default-mode)))))) + +(defun VirTeX-common-initialization () + "Perform basic initialization." + (kill-all-local-variables) + (setq TeX-mode-p t) + (setq TeX-output-extension (if TeX-PDF-mode "pdf" "dvi")) + (setq local-abbrev-table text-mode-abbrev-table) + (setq indent-tabs-mode nil) + + ;; Ispell support + (make-local-variable 'ispell-parser) + (setq ispell-parser 'tex) + (make-local-variable 'ispell-tex-p) + (setq ispell-tex-p t) + + ;; Redefine some standard variables + (make-local-variable 'paragraph-start) + (make-local-variable 'paragraph-separate) + (make-local-variable 'comment-start) + (setq comment-start "%") + (make-local-variable 'comment-start-skip) + (setq comment-start-skip + (concat + "\\(\\(^\\|[^\\\n]\\)\\(" + (regexp-quote TeX-esc) + (regexp-quote TeX-esc) + "\\)*\\)\\(%+[ \t]*\\)")) + (set (make-local-variable 'comment-end-skip) "[ \t]*\\(\\s>\\|\n\\)") + (set (make-local-variable 'comment-use-syntax) t) + ;; `comment-padding' is defined here as an integer for compatibility + ;; reasons because older Emacsen could not cope with a string. + (make-local-variable 'comment-padding) + (setq comment-padding 1) + ;; Removed as commenting in (La)TeX is done with one `%' not two + ;; (make-local-variable 'comment-add) + ;; (setq comment-add 1) ;default to `%%' in comment-region + (make-local-variable 'comment-indent-function) + (setq comment-indent-function 'TeX-comment-indent) + (make-local-variable 'comment-multi-line) + (setq comment-multi-line nil) + (make-local-variable 'compile-command) + (unless (boundp 'compile-command) + (setq compile-command "make")) + (make-local-variable 'words-include-escapes) + (setq words-include-escapes nil) + + ;; Make TAB stand out + ;; (make-local-variable 'buffer-display-table) + ;; (setq buffer-display-table (if standard-display-table + ;; (copy-sequence standard-display-table) + ;; (make-display-table))) + ;; (aset buffer-display-table ?\t (apply 'vector (append "" nil))) + + ;; Symbol completion. + (make-local-variable 'TeX-complete-list) + (setq TeX-complete-list + (list (list "\\\\\\([a-zA-Z]*\\)" + 1 'TeX-symbol-list (if TeX-insert-braces "{}")) + (list "" TeX-complete-word))) + + (funcall TeX-install-font-lock) + + ;; We want this to be early in the list, so we do not add it before + ;; we enter TeX mode the first time. + (if (boundp 'local-write-file-hooks) + (add-hook 'local-write-file-hooks 'TeX-safe-auto-write) + (add-hook 'write-file-hooks 'TeX-safe-auto-write)) + (make-local-variable 'TeX-auto-update) + (setq TeX-auto-update t) + + ;; Minor modes + (when TeX-source-correlate-mode + (TeX-source-correlate-mode 1)) + + ;; Let `TeX-master-file' be called after a new file was opened and + ;; call `TeX-update-style' on any file opened. (The addition to the + ;; hook has to be made here because its local value will be deleted + ;; by `kill-all-local-variables' if it is added e.g. in `tex-mode'.) + ;; + ;; `TeX-update-style' has to be called before + ;; `global-font-lock-mode', which may also be specified in + ;; `find-file-hooks', gets called. Otherwise style-based + ;; fontification will break (in XEmacs). That means, `add-hook' + ;; cannot be called with a non-nil value of the APPEND argument. + ;; + ;; `(TeX-master-file nil nil t)' has to be called *before* + ;; `TeX-update-style' as the latter will call `TeX-master-file' + ;; without the `ask' bit set. + (when (and (featurep 'xemacs) (not (emacs-version>= 21 5))) + (make-local-hook 'find-file-hooks)) + (add-hook 'find-file-hooks + (lambda () + ;; Check if we are looking at a new or shared file. + (when (or (not (file-exists-p (buffer-file-name))) + (eq TeX-master 'shared)) + (TeX-master-file nil nil t)) + (TeX-update-style t)) nil t)) + +;;; Plain TeX mode + +(defcustom plain-TeX-clean-intermediate-suffixes + TeX-clean-default-intermediate-suffixes + "List of regexps matching suffixes of intermediate files to be deleted. +The regexps will be anchored at the end of the file name to be matched, +i.e. you do _not_ have to cater for this yourself by adding \\\\' or $." + :type '(repeat regexp) + :group 'TeX-command) + +(defcustom plain-TeX-clean-output-suffixes TeX-clean-default-output-suffixes + "List of regexps matching suffixes of output files to be deleted. +The regexps will be anchored at the end of the file name to be matched, +i.e. you do _not_ have to cater for this yourself by adding \\\\' or $." + :type '(repeat regexp) + :group 'TeX-command) + +(defcustom plain-TeX-mode-hook nil + "A hook run in plain TeX mode buffers." + :type 'hook + :group 'TeX-misc) + +;;;###autoload +(defun TeX-plain-tex-mode () + "Major mode in AUCTeX for editing plain TeX files. +See info under AUCTeX for documentation. + +Special commands: +\\{plain-TeX-mode-map} + +Entering `plain-tex-mode' calls the value of `text-mode-hook', +then the value of `TeX-mode-hook', and then the value +of plain-TeX-mode-hook." + (interactive) + (plain-TeX-common-initialization) + (setq major-mode 'plain-tex-mode) + (use-local-map plain-TeX-mode-map) + (easy-menu-add plain-TeX-mode-menu plain-TeX-mode-map) + (easy-menu-add plain-TeX-mode-command-menu plain-TeX-mode-map) + (setq TeX-base-mode-name "TeX") + (setq TeX-command-default "TeX") + (setq TeX-sentinel-default-function 'TeX-TeX-sentinel) + (add-hook 'tool-bar-mode-on-hook 'plain-TeX-maybe-install-toolbar nil t) + (when (if (featurep 'xemacs) + (featurep 'toolbar) + (and (boundp 'tool-bar-mode) tool-bar-mode)) + (plain-TeX-maybe-install-toolbar)) + (TeX-run-mode-hooks 'text-mode-hook 'TeX-mode-hook 'plain-TeX-mode-hook) + (TeX-set-mode-name)) + +(defun plain-TeX-common-initialization () + "Common initialization for plain TeX like modes." + (VirTeX-common-initialization) + (set-syntax-table TeX-mode-syntax-table) + (setq paragraph-start + (concat + "\\(^[ \t]*$" + "\\|" (regexp-quote TeX-esc) "par\\|" + "^[ \t]*" + (regexp-quote TeX-esc) + "\\(" + "begin\\|end\\|part\\|chapter\\|" + "section\\|subsection\\|subsubsection\\|" + "paragraph\\|include\\|includeonly\\|" + "tableofcontents\\|appendix\\|label\\|caption\\|" + "\\[\\|\\]" ; display math delimitors + "\\)" + "\\|" + "^[ \t]*\\$\\$" ; display math delimitor + "\\)" )) + (setq paragraph-separate + (concat + "[ \t]*" + "\\(" + (regexp-quote TeX-esc) "par\\|" + "%\\|" + "$\\|" + "\\$\\$\\|" + (regexp-quote TeX-esc) + "\\(" + "begin\\|end\\|label\\|caption\\|part\\|chapter\\|" + "section\\|subsection\\|subsubsection\\|" + "paragraph\\|include\\|includeonly\\|" + "tableofcontents\\|appendix\\|" (regexp-quote TeX-esc) + "\\)" + "\\)")) + (setq TeX-header-end (regexp-quote "%**end of header")) + (setq TeX-trailer-start (regexp-quote (concat TeX-esc "bye"))) + (TeX-run-style-hooks "TEX")) + +;;; Hilighting + +(if (boundp 'hilit-patterns-alist) + (let ((latex-patterns (cdr-safe (assq 'latex-mode hilit-patterns-alist))) + (plain-tex-patterns (cdr-safe (assq 'plain-tex-mode + hilit-patterns-alist)))) + (if (and latex-patterns plain-tex-patterns) + (setq hilit-patterns-alist + (append (list (cons 'ams-tex-mode plain-tex-patterns)) + hilit-patterns-alist))))) + +;;; Parsing + +(defgroup TeX-parse nil + "Parsing TeX files from AUCTeX." + :group 'AUCTeX) + +(defvar TeX-auto-parser '((styles TeX-auto-file TeX-run-style-hooks))) +;; Alist of parsed information. +;; Each entry is a list with the following elements: +;; +;; 0. Name of information type. +;; 1. Name of temporary variable used when parsing. +;; 2. Name of function to add information to add to #3. +;; 3. Name of variable holding buffer local information. +;; 4. Name of variable indicating that #3 has changed. + + +(defconst TeX-auto-parser-temporary 1) +(defconst TeX-auto-parser-add 2) +(defconst TeX-auto-parser-local 3) +(defconst TeX-auto-parser-change 4) + +(defun TeX-auto-add-type (name prefix &optional plural) + "Add information about NAME to the parser using PREFIX. + +Optional third argument PLURAL is the plural form of TYPE. +By default just add an `s'. + +This function create a set of variables and functions to maintain a +separate type of information in the parser." + (let* ((names (or plural (concat name "s"))) + (tmp (intern (concat prefix "-auto-" name))) + (add (intern (concat prefix "-add-" names))) + (local (intern (concat prefix "-" name "-list"))) + (change (intern (concat prefix "-" name "-changed")))) + (setq TeX-auto-parser + (cons (list name tmp add local change) TeX-auto-parser)) + (set local nil) + (make-variable-buffer-local local) + (set change nil) + (make-variable-buffer-local change) + (fset add `(lambda (&rest entries) + ,(concat "Add information about " (upcase name) + " to the current buffer. +Generated by `TeX-auto-add-type'.") + (TeX-auto-add-information ,name entries))) + (fset local `(lambda nil + ,(concat "List of " names + " active in the current buffer. +Generated by `TeX-auto-add-type'.") + (TeX-auto-list-information ,name))) + (add-hook 'TeX-remove-style-hook + `(lambda nil (setq ,(symbol-name local) nil))))) + +(defun TeX-auto-add-information (name entries) + "For NAME in `TeX-auto-parser' add ENTRIES." + (let* ((entry (assoc name TeX-auto-parser)) + (change (nth TeX-auto-parser-change entry)) + (change-value (symbol-value change)) + (local (nth TeX-auto-parser-local entry)) + (local-value (symbol-value local))) + (if change-value + (set local (cons entries local-value)) + (set change t) + (set local (list entries local-value))))) + +(defun TeX-auto-list-information (name) + "Return information in `TeX-auto-parser' about NAME." + (TeX-update-style) + (let* ((entry (assoc name TeX-auto-parser)) + (change (nth TeX-auto-parser-change entry)) + (change-value (symbol-value change)) + (local (nth TeX-auto-parser-local entry))) + (if (not change-value) + () + (set change nil) + ;; Sort it + (message "Sorting %s..." name) + (set local + (sort (mapcar 'TeX-listify (apply 'append (symbol-value local))) + 'TeX-car-string-lessp)) + ;; Make it unique + (message "Removing duplicates...") + (let ((entry (symbol-value local))) + (while (and entry (cdr entry)) + (let ((this (car entry)) + (next (car (cdr entry)))) + (if (not (string-equal (car this) (car next))) + (setq entry (cdr entry)) + ;; We have two equal symbols. Use the one with + ;; most arguments. + (if (> (length next) (length this)) + (setcdr this (cdr next))) + (setcdr entry (cdr (cdr entry))))))) + (message "Removing duplicates... done")) + (symbol-value local))) + +(TeX-auto-add-type "symbol" "TeX") + +(defvar TeX-auto-apply-hook nil + "Hook run when a buffer is parsed and the information is applied.") + +(defun TeX-auto-apply () + "Parse and apply TeX information in the current buffer." + (TeX-auto-parse) + (run-hooks 'TeX-auto-apply-hook) + (mapcar 'TeX-auto-apply-entry TeX-auto-parser)) + +(defun TeX-auto-apply-entry (entry) + "Apply the information in ENTRY in `TeX-auto-parser'." + (let ((value (symbol-value (nth TeX-auto-parser-temporary entry))) + (add (nth TeX-auto-parser-add entry))) + (if value (apply add value)))) + +(defun TeX-safe-auto-write () + "Call `TeX-auto-write' safely." + (condition-case name + (and (boundp 'TeX-auto-update) + TeX-auto-update + (TeX-auto-write)) + (error nil)) + ;; Continue with the other write file hooks. + nil) + +(defcustom TeX-auto-save nil + "*Automatically save style information when saving the buffer." + :group 'TeX-parse + :type 'boolean) + +(defcustom TeX-auto-untabify nil + "*Automatically untabify when saving the buffer." + :group 'TeX-parse + :type 'boolean) + +(defun TeX-auto-write () + "Save all relevant TeX information from the current buffer." + (if TeX-auto-untabify + (untabify (point-min) (point-max))) + (if (and TeX-auto-save TeX-auto-local) + (let* ((file (expand-file-name + (concat + (file-name-as-directory TeX-auto-local) + (TeX-strip-extension nil TeX-all-extensions t) + ".el") + (TeX-master-directory))) + (dir (file-name-directory file))) + ;; Create auto directory if possible. + (if (not (file-exists-p dir)) + (condition-case name + (make-directory dir) + (error nil))) + (if (file-writable-p file) + (save-excursion + (TeX-update-style) + (TeX-auto-store file)) + (message "Can't write style information."))))) + +(defcustom TeX-macro-default (car-safe TeX-macro-private) + "*Default directory to search for TeX macros." + :group 'TeX-file + :type 'directory) + +(defcustom TeX-auto-default (car-safe TeX-auto-private) + "*Default directory to place automatically generated TeX information." + :group 'TeX-file + :type 'directory) + +;;;###autoload +(defun TeX-auto-generate (tex auto) + "Generate style file for TEX and store it in AUTO. +If TEX is a directory, generate style files for all files in the directory." + (interactive (list (setq TeX-macro-default + (expand-file-name (read-file-name + "TeX file or directory: " + TeX-macro-default + TeX-macro-default 'confirm))) + (setq TeX-auto-default + (expand-file-name (read-file-name + "AUTO lisp directory: " + TeX-auto-default + TeX-auto-default 'confirm))))) + (cond ((not (file-readable-p tex))) + ((string-match TeX-ignore-file tex)) + ((file-directory-p tex) + (let ((files (directory-files (expand-file-name tex))) + (default-directory (file-name-as-directory + (expand-file-name tex))) + (TeX-file-recurse (cond ((symbolp TeX-file-recurse) + TeX-file-recurse) + ((zerop TeX-file-recurse) + nil) + ((1- TeX-file-recurse))))) + (mapcar (lambda (file) + (if (or TeX-file-recurse + (not (file-directory-p file))) + (TeX-auto-generate file auto))) + files))) + ((not (file-newer-than-file-p + tex + (concat (file-name-as-directory auto) + (TeX-strip-extension tex TeX-all-extensions t) + ".el")))) + ((TeX-match-extension tex (append TeX-file-extensions + BibTeX-file-extensions)) + (save-excursion + (set-buffer (let (enable-local-eval) + (find-file-noselect tex))) + (message "Parsing %s..." tex) + (TeX-auto-store (concat (file-name-as-directory auto) + (TeX-strip-extension tex + TeX-all-extensions + t) + ".el")) + (kill-buffer (current-buffer)) + (message "Parsing %s... done" tex))))) + +;;;###autoload +(defun TeX-auto-generate-global () + "Create global auto directory for global TeX macro definitions." + (interactive) + (unless (file-directory-p TeX-auto-global) + (make-directory TeX-auto-global)) + (let ((TeX-file-extensions '("cls" "sty")) + (BibTeX-file-extensions nil)) + (mapc (lambda (macro) (TeX-auto-generate macro TeX-auto-global)) + TeX-macro-global)) + (byte-recompile-directory TeX-auto-global 0)) + +(defun TeX-auto-store (file) + "Extract information for AUCTeX from current buffer and store it in FILE." + (TeX-auto-parse) + + (if (member nil (mapcar 'TeX-auto-entry-clear-p TeX-auto-parser)) + (let ((style (TeX-strip-extension nil TeX-all-extensions t))) + (TeX-unload-style style) + (save-excursion + (set-buffer (generate-new-buffer file)) + (erase-buffer) + (insert "(TeX-add-style-hook \"" style "\"\n" + " (lambda ()") + (mapc (lambda (el) (TeX-auto-insert el style)) + TeX-auto-parser) + (insert "))\n\n") + (write-region (point-min) (point-max) file nil 'silent) + (kill-buffer (current-buffer)))) + (if (file-exists-p (concat file "c")) + (delete-file (concat file "c"))) + (if (file-exists-p file) + (delete-file file)))) + +(defun TeX-auto-entry-clear-p (entry) + "Check if the temporary for `TeX-auto-parser' entry ENTRY is clear." + ;; FIXME: This doc-string isn't clear to me. -- rs + (null (symbol-value (nth TeX-auto-parser-temporary entry)))) + +(defun TeX-auto-insert (entry &optional skip) + "Insert code to initialize ENTRY from `TeX-auto-parser'. + +If SKIP is not-nil, don't insert code for SKIP." + (let ((name (symbol-name (nth TeX-auto-parser-add entry))) + (list (symbol-value (nth TeX-auto-parser-temporary entry)))) + (unless (null list) + (insert "\n (" name) + (dolist (el list) + (cond ((and (stringp el) (not (string= el skip))) + (insert "\n ") + (insert (prin1-to-string el))) + ((not (stringp el)) + (insert "\n ") + (insert "'" (prin1-to-string el))))) + (insert ")")))) + +(defvar TeX-auto-ignore + '("csname" "filedate" "fileversion" "docdate" "next" "labelitemi" + "labelitemii" "labelitemiii" "labelitemiv" "labelitemv" + "labelenumi" "labelenumii" "labelenumiii" "labelenumiv" + "labelenumv" "theenumi" "theenumii" "theenumiii" "theenumiv" + "theenumv" "document" "par" "do" "expandafter") + "List of symbols to ignore when scanning a TeX style file.") + +(defun TeX-auto-add-regexp (regexp) + "Add REGEXP to `TeX-auto-regexp-list' if not already a member." + (if (symbolp TeX-auto-regexp-list) + (setq TeX-auto-regexp-list (symbol-value TeX-auto-regexp-list))) + (or (memq regexp TeX-auto-regexp-list) + (setq TeX-auto-regexp-list (cons regexp TeX-auto-regexp-list)))) + +(defvar TeX-auto-empty-regexp-list + '(("\\(\\'\\`\\)" 1 ignore)) + "List of regular expressions guaranteed to match nothing.") + +(defvar TeX-token-char + (if (featurep 'mule) + "\\(?:[a-zA-Z]\\|\\cj\\)" + "[a-zA-Z]") + "Regexp matching a character in a TeX macro. + +Please use a shy group if you use a grouping construct, because +the functions/variables which use `TeX-token-char' expect not to +alter the numbering of any ordinary, non-shy groups.") + +(defvar plain-TeX-auto-regexp-list + (let ((token TeX-token-char)) + `((,(concat "\\\\def\\\\\\(" token "+\\)[^a-zA-Z@]") 1 TeX-auto-symbol-check) + (,(concat "\\\\let\\\\\\(" token "+\\)[^a-zA-Z@]") 1 TeX-auto-symbol-check) + (,(concat "\\\\font\\\\\\(" token "+\\)[^a-zA-Z@]") 1 TeX-auto-symbol) + (,(concat "\\\\chardef\\\\\\(" token "+\\)[^a-zA-Z@]") 1 TeX-auto-symbol) + (,(concat "\\\\new\\(?:count\\|dimen\\|muskip\\|skip\\)\\\\\\(" token "+\\)[^a-zA-Z@]") + 1 TeX-auto-symbol) + (,(concat "\\\\newfont{?\\\\\\(" token "+\\)}?") 1 TeX-auto-symbol) + (,(concat "\\\\typein\\[\\\\\\(" token "+\\)\\]") 1 TeX-auto-symbol) + ("\\\\input +\\(\\.*[^#%\\\\\\.\n\r]+\\)\\(\\.[^#%\\\\\\.\n\r]+\\)?" + 1 TeX-auto-file) + (,(concat "\\\\mathchardef\\\\\\(" token "+\\)[^a-zA-Z@]") 1 TeX-auto-symbol))) + "List of regular expression matching common LaTeX macro definitions.") + +(defvar TeX-auto-full-regexp-list plain-TeX-auto-regexp-list + "Full list of regular expression matching TeX macro definitions.") + +(defvar TeX-auto-prepare-hook nil + "List of hooks to be called before parsing a TeX file.") + +(defvar TeX-auto-cleanup-hook nil + "List of hooks to be called after parsing a TeX file.") + +(defcustom TeX-auto-parse-length 999999 + "Maximal length of TeX file (in characters) that will be parsed." + :group 'TeX-parse + :type 'integer) + (make-variable-buffer-local 'TeX-auto-parse-length) + +(defcustom TeX-auto-x-parse-length 0 + "Maximum length of TeX file that will be parsed additionally. +Use `TeX-auto-x-regexp-list' for parsing the region between +`TeX-auto-parse-length' and this value." + :group 'TeX-parse + :type 'integer) + (make-variable-buffer-local 'TeX-auto-x-parse-length) + +(defcustom TeX-auto-x-regexp-list 'LaTeX-auto-label-regexp-list + "List of regular expressions used for additional parsing. +See `TeX-auto-x-parse-length'." + :type '(radio (variable-item TeX-auto-empty-regexp-list) + (variable-item TeX-auto-full-regexp-list) + (variable-item plain-TeX-auto-regexp-list) + (variable-item LaTeX-auto-minimal-regexp-list) + (variable-item LaTeX-auto-label-regexp-list) + (variable-item LaTeX-auto-regexp-list) + (symbol :tag "Other") + (repeat :tag "Specify" + (group (regexp :tag "Match") + (sexp :tag "Groups") + symbol))) + :group 'TeX-parse) + (make-variable-buffer-local 'TeX-auto-x-regexp-list) + +(defun TeX-regexp-group-count (regexp) + "Return number of groups in a REGEXP. This is not foolproof: +you should not use something like `[\\(]' for a character range." + (let (start (n 0)) + (while (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\\\([^?]" + regexp start) + (setq start (- (match-end 0) 2) + n (1+ n))) + n)) + +(defun TeX-auto-parse-region (regexp-list beg end) + "Parse TeX information according to REGEXP-LIST between BEG and END." + (if (symbolp regexp-list) + (setq regexp-list (and (boundp regexp-list) (symbol-value regexp-list)))) + (if regexp-list + ;; Extract the information. + (let* (groups + (count 1) + (regexp (concat "\\(" + (mapconcat + (lambda(x) + (push (cons count x) groups) + (setq count + (+ 1 count + (TeX-regexp-group-count (car x)))) + (car x)) + regexp-list "\\)\\|\\(") + "\\)")) + syms + lst) + (setq count 0) + (goto-char (if end (min end (point-max)) (point-max))) + (while (re-search-backward regexp beg t) + (let* ((entry (cdr (TeX-member nil groups + (lambda (a b) + (match-beginning (car b)))))) + (symbol (nth 2 entry)) + (match (nth 1 entry))) + (unless (TeX-in-comment) + (looking-at (nth 0 entry)) + (if (fboundp symbol) + (funcall symbol match) + (puthash (if (listp match) + (mapcar #'TeX-match-buffer match) + (TeX-match-buffer match)) + (setq count (1- count)) + (cdr (or (assq symbol syms) + (car (push + (cons symbol + (make-hash-table :test 'equal)) + syms))))))))) + (setq count 0) + (dolist (symbol syms) + (setq lst (symbol-value (car symbol))) + (while lst + (puthash (pop lst) + (setq count (1+ count)) + (cdr symbol))) + (maphash (lambda (key value) + (push (cons value key) lst)) + (cdr symbol)) + (clrhash (cdr symbol)) + (set (car symbol) (mapcar #'cdr (sort lst #'car-less-than-car))))))) + + +(defun TeX-auto-parse () + "Parse TeX information in current buffer. + +Call the functions in `TeX-auto-prepare-hook' before parsing, and the +functions in `TeX-auto-cleanup-hook' after parsing." + + (let ((case-fold-search nil)) + + (mapc 'TeX-auto-clear-entry TeX-auto-parser) + (run-hooks 'TeX-auto-prepare-hook) + + (save-excursion + (and (> TeX-auto-x-parse-length TeX-auto-parse-length) + (> (point-max) TeX-auto-parse-length) + (TeX-auto-parse-region TeX-auto-x-regexp-list + TeX-auto-parse-length + TeX-auto-x-parse-length)) + (TeX-auto-parse-region TeX-auto-regexp-list + nil TeX-auto-parse-length)) + + ;; Cleanup ignored symbols. + + ;; NOTE: This is O(N M) where it could be O(N log N + M log M) if we + ;; sorted the lists first. + (while (member (car TeX-auto-symbol) TeX-auto-ignore) + (setq TeX-auto-symbol (cdr TeX-auto-symbol))) + (let ((list TeX-auto-symbol)) + (while (and list (cdr list)) + (if (member (car (cdr list)) TeX-auto-ignore) + (setcdr list (cdr (cdr list))) + (setq list (cdr list))))) + + (run-hooks 'TeX-auto-cleanup-hook))) + +(defun TeX-auto-clear-entry (entry) + "Set the temporary variable in ENTRY to nil." + (set (nth TeX-auto-parser-temporary entry) nil)) + +(defvar LaTeX-auto-end-symbol nil) + +(defun TeX-auto-symbol-check (match) + "Add MATCH to TeX-auto-symbols. +Check for potential LaTeX environments." + (let ((symbol (if (listp match) + (mapcar 'TeX-match-buffer match) + (TeX-match-buffer match)))) + (if (and (stringp symbol) + (string-match "^end\\(.+\\)$" symbol)) + (add-to-list 'LaTeX-auto-end-symbol + (substring symbol (match-beginning 1) (match-end 1))) + (if (listp symbol) + (dolist (elt symbol) + (add-to-list 'TeX-auto-symbol elt)) + (add-to-list 'TeX-auto-symbol symbol))))) + +;;; Utilities +;; +;; Some of these functions has little to do with TeX, but nonetheless we +;; should use the "TeX-" prefix to avoid name clashes. + +(defcustom TeX-auto-regexp-list 'TeX-auto-full-regexp-list + "*List of regular expressions used for parsing the current file." + :type '(radio (variable-item TeX-auto-empty-regexp-list) + (variable-item TeX-auto-full-regexp-list) + (variable-item plain-TeX-auto-regexp-list) + (variable-item LaTeX-auto-minimal-regexp-list) + (variable-item LaTeX-auto-label-regexp-list) + (variable-item LaTeX-auto-regexp-list) + (symbol :tag "Other") + (repeat :tag "Specify" + (group (regexp :tag "Match") + (sexp :tag "Groups") + symbol))) + :group 'TeX-parse) + (make-variable-buffer-local 'TeX-auto-regexp-list) + +(defgroup TeX-file-extension nil + "File extensions recognized by AUCTeX." + :group 'TeX-file) + +(defcustom TeX-file-extensions '("tex" "sty" "cls" "ltx" "texi" "texinfo" "dtx") + "*File extensions used by manually generated TeX files." + :group 'TeX-file-extension + :type '(repeat (string :format "%v"))) + +(defcustom TeX-all-extensions '("[^.\n]+") + "All possible file extensions." + :group 'TeX-file-extension + :type '(repeat (regexp :format "%v"))) + +(defcustom TeX-default-extension "tex" + "*Default extension for TeX files." + :group 'TeX-file-extension + :type 'string) + + (make-variable-buffer-local 'TeX-default-extension) + +(defcustom docTeX-default-extension "dtx" + "*Default extension for docTeX files." + :group 'TeX-file-extension + :type 'string) + +(defvar TeX-output-extension nil + "Extension of TeX output file. +This is either a string or a list with +a string as element. Its value is obtained from `TeX-command-output-list'. +Access to the value should be through the function `TeX-output-extension'.") + + (make-variable-buffer-local 'TeX-output-extension) + +(defcustom BibTeX-file-extensions '("bib") + "Valid file extensions for BibTeX files." + :group 'TeX-file-extension + :type '(repeat (string :format "%v"))) + +(defcustom BibTeX-style-extensions '("bst") + "Valid file extensions for BibTeX styles." + :group 'TeX-file-extension + :type '(repeat (string :format "%v"))) + +(defcustom TeX-ignore-file "\\(^\\|[/\\]\\)\\(\\.\\|\\.\\.\\|RCS\\|SCCS\\|CVS\\|babel\\..*\\)$" + "Regular expression matching file names to ignore. + +These files or directories will not be considered when searching for +TeX files in a directory." + :group 'TeX-parse + :type 'regexp) + +(defcustom TeX-file-recurse t + "*Whether to search TeX directories recursively. +nil means do not recurse, a positive integer means go that far deep in the +directory hierarchy, t means recurse indefinitely." + :group 'TeX-parse + :type '(choice (const :tag "On" t) + (const :tag "Off" nil) + (integer :tag "Depth" :value 1))) + +(defun TeX-match-extension (file &optional extensions) + "Return non-nil if FILE has one of EXTENSIONS. + +If EXTENSIONS is not specified or nil, the value of +`TeX-file-extensions' is used instead." + + (if (null extensions) + (setq extensions TeX-file-extensions)) + + (let ((regexp (concat "\\.\\(" + (mapconcat 'identity extensions "\\|") + "\\)$")) + (case-fold-search t)) + (string-match regexp file))) + +(defun TeX-strip-extension (&optional string extensions nodir nostrip) + "Return STRING without any trailing extension in EXTENSIONS. +If NODIR is t, also remove directory part of STRING. +If NODIR is `path', remove directory part of STRING if it is equal to +the current directory, `TeX-macro-private' or `TeX-macro-global'. +If NOSTRIP is set, do not remove extension after all. +STRING defaults to the name of the current buffer. +EXTENSIONS defaults to `TeX-file-extensions'." + + (if (null string) + (setq string (or (buffer-file-name) ""))) + + (if (null extensions) + (setq extensions TeX-file-extensions)) + + (let* ((strip (if (and (not nostrip) + (TeX-match-extension string extensions)) + (substring string 0 (match-beginning 0)) + string)) + (dir (expand-file-name (or (file-name-directory strip) "./")))) + (if (or (eq nodir t) + (string-equal dir (expand-file-name "./")) + (member dir TeX-macro-global) + (member dir TeX-macro-private)) + (file-name-nondirectory strip) + strip))) + +(defcustom TeX-kpathsea-path-delimiter t + "Path delimiter for kpathsea output. +t means autodetect, nil means kpathsea is disabled." + :group 'TeX-file + :type '(choice (const ":") + (const ";") + (const :tag "Autodetect" t) + (const :tag "Off" nil))) + +(defcustom TeX-kpathsea-format-alist + '(("tex" "${TEXINPUTS.latex}" TeX-file-extensions) + ("sty" "${TEXINPUTS.latex}" '("sty")) + ("dvi" "${TEXDOCS}" '("dvi" "pdf" "ps" "txt" "html" + "dvi.gz" "pdf.gz" "ps.gz" "txt.gz" "html.gz" + "dvi.bz2" "pdf.bz2" "ps.bz2" "txt.bz2" "html.bz2")) + ("eps" "${TEXINPUTS}" LaTeX-includegraphics-extensions) + ("pdf" "${TEXINPUTS}" LaTeX-includegraphics-extensions) + ("png" "${TEXINPUTS}" LaTeX-includegraphics-extensions) + ("jpg" "${TEXINPUTS}" LaTeX-includegraphics-extensions) + ("jpeg" "${TEXINPUTS}" LaTeX-includegraphics-extensions) + ("bib" "$BIBINPUTS" BibTeX-file-extensions) + ("bst" "$BSTINPUTS" BibTeX-style-extensions)) + "Formats to search for expansion using kpathsea. +The key of the alist represents the name of the format. The +first element of the cdr of the alist is string to expand by the +respective kpathsea program and the second element is a list of +file extensions to match." + :group 'TeX-file + :type '(alist :key-type string :value-type (group string sexp))) + +;; FIXME: Despite the first parameter named `extensions', +;; `TeX-search-files-kpathsea' basically treats this as a format +;; specifier. Only the first element in the respective list will be +;; used to determine the search paths and file extensions with the +;; help of `TeX-kpathsea-format-alist'. Out of these differences +;; arises a need to unify the behavior of `TeX-search-files' and +;; `TeX-search-files-kpathsea' and their treatment of parameters. +;; Additionally `TeX-search-files-kpathsea' should be made more +;; general to work with other platforms and TeX systems as well. +(defun TeX-search-files-kpathsea (extensions nodir strip) + "The kpathsea-enabled version of `TeX-search-files'. +Except for DIRECTORIES (a kpathsea string), the arguments for +EXTENSIONS, NODIR and STRIP are explained there." + (and TeX-kpathsea-path-delimiter + (catch 'no-kpathsea + (let* ((format-spec (assoc (car extensions) + TeX-kpathsea-format-alist)) + (dirs (with-output-to-string + (unless (zerop + (call-process + "kpsewhich" nil (list standard-output nil) + nil + (concat + "-expand-path=" + (nth 1 format-spec)))) + (if (eq TeX-kpathsea-path-delimiter t) + (throw 'no-kpathsea + (setq TeX-kpathsea-path-delimiter nil)) + (error "kpsewhich error"))))) + result) + (when (eq TeX-kpathsea-path-delimiter t) + (setq TeX-kpathsea-path-delimiter + (cond ((string-match ";" dirs) + ";") + ((string-match ":" dirs) + ":")))) + (unless TeX-kpathsea-path-delimiter + (throw 'no-kpathsea nil)) + (setq dirs (split-string dirs (concat "[\n\r" + TeX-kpathsea-path-delimiter + "]+"))) + (setq extensions (concat "\\." + (regexp-opt (eval (nth 2 format-spec)) t) + "\\'")) + (setq result + (apply #'append + (mapcar + (lambda(x) (directory-files x + (not nodir) + extensions)) + dirs))) + (if strip + (mapcar (lambda(x) + (if (string-match extensions x) + (substring x 0 (match-beginning 0)) + x)) + result) + result))))) + +(defun TeX-search-files (&optional directories extensions nodir strip) + "Return a list of all reachable files in DIRECTORIES ending with EXTENSIONS. +If optional argument NODIR is set, remove directory part. +If optional argument STRIP is set, remove file extension. +If optional argument DIRECTORIES is set, search in those directories. +Otherwise, search in all TeX macro directories. +If optional argument EXTENSIONS is not set, use `TeX-file-extensions'" + (if (null extensions) + (setq extensions TeX-file-extensions)) + (or (TeX-search-files-kpathsea extensions nodir strip) + (progn + (if (null directories) + (setq directories + (cons "./" (append TeX-macro-private TeX-macro-global)))) + (let (match + (TeX-file-recurse (cond ((symbolp TeX-file-recurse) + TeX-file-recurse) + ((zerop TeX-file-recurse) + nil) + ((1- TeX-file-recurse))))) + (while directories + (let* ((directory (car directories)) + (content (and directory + (file-readable-p directory) + (file-directory-p directory) + (directory-files directory)))) + (setq directories (cdr directories)) + (while content + (let ((file (concat directory (car content)))) + (setq content (cdr content)) + (cond ((string-match TeX-ignore-file file)) + ((not (file-readable-p file))) + ((file-directory-p file) + (if TeX-file-recurse + (setq match + (append match + (TeX-search-files + (list (file-name-as-directory file)) + extensions + nodir strip))))) + ((TeX-match-extension file extensions) + (setq match (cons (TeX-strip-extension file + extensions + nodir + (not strip)) + match)))))))) + match)))) + +(defun TeX-car-string-lessp (s1 s2) + "Compare the cars of S1 and S2 in lexicographic order. +Return t if first is less than second in lexicographic order." + (string-lessp (car s1) (car s2))) + +(defun TeX-listify (elt) + "Return a newly created list with element ELT. +If ELT already is a list, return ELT." + (if (listp elt) elt (list elt))) + +(defun TeX-member (elt list how) + "Return the member ELT in LIST. Comparison done with HOW. +Return nil if ELT is not a member of LIST." + (while (and list (not (funcall how elt (car list)))) + (setq list (cdr list))) + (car-safe list)) + +(defun TeX-elt-of-list-member (elts list) + "Return non-nil if an element of ELTS is a member of LIST." + (catch 'found + (dolist (elt elts) + (when (member elt list) + (throw 'found t))))) + +(defun TeX-assoc (key list) + "Return non-nil if KEY is `equal' to the car of an element of LIST. +Like assoc, except case insensitive." + (let ((case-fold-search t)) + (TeX-member key list + (lambda (a b) + (string-match (concat "^" (regexp-quote a) "$") + (car b)))))) + +(defun TeX-match-buffer (n) + "Return the substring corresponding to the N'th match. +See `match-data' for details." + (if (match-beginning n) + (buffer-substring-no-properties (match-beginning n) (match-end n)) + "")) + +(defun TeX-function-p (arg) + "Return non-nil if ARG is callable as a function." + (or (and (fboundp 'byte-code-function-p) + (byte-code-function-p arg)) + (and (listp arg) + (eq (car arg) 'lambda)) + (and (symbolp arg) + (fboundp arg)))) + +(defun TeX-booleanp (arg) + "Return non-nil if ARG is t or nil." + (memq arg '(t nil))) + +(defun TeX-looking-at-backward (regexp &optional limit) + "Return non-nil if the text before point matches REGEXP. +Optional second argument LIMIT gives a max number of characters +to look backward for." + (let ((pos (point))) + (save-excursion + (and (re-search-backward regexp + (if limit (max (point-min) (- (point) limit))) + t) + (eq (match-end 0) pos))))) + +(defun TeX-current-line () + "The current line number." + (format "%d" (1+ (TeX-current-offset)))) + +(defun TeX-current-file-name-master-relative () + "Return current filename, relative to master directory." + (file-relative-name + (buffer-file-name) + (TeX-master-directory))) + +(defun TeX-near-bobp () + "Return t iff there's nothing but whitespace between (bob) and (point)." + (save-excursion + (skip-chars-backward " \t\n") + (bobp))) + +(defun TeX-deactivate-mark () + "Deactivate the mark. +This is a compatibility function which works both in Emacs and +XEmacs. In XEmacs the region is deactivated instead of the +mark which is sort of equivalent." + (if (featurep 'xemacs) + (zmacs-deactivate-region) + (deactivate-mark))) + +(defalias 'TeX-run-mode-hooks + (if (fboundp 'run-mode-hooks) 'run-mode-hooks 'run-hooks)) + + +;;; Syntax Table + +(defvar TeX-mode-syntax-table (make-syntax-table) + "Syntax table used while in TeX mode.") + + (make-variable-buffer-local 'TeX-mode-syntax-table) + +(progn ; Define TeX-mode-syntax-table. + (modify-syntax-entry (string-to-char TeX-esc) + "\\" TeX-mode-syntax-table) + (modify-syntax-entry ?\f ">" TeX-mode-syntax-table) + (modify-syntax-entry ?\n ">" TeX-mode-syntax-table) + (modify-syntax-entry (string-to-char TeX-grop) + (concat "(" TeX-grcl) + TeX-mode-syntax-table) + (modify-syntax-entry (string-to-char TeX-grcl) + (concat ")" TeX-grop) + TeX-mode-syntax-table) + (modify-syntax-entry ?% "<" TeX-mode-syntax-table) + (modify-syntax-entry ?\" "." TeX-mode-syntax-table) + (modify-syntax-entry ?& "." TeX-mode-syntax-table) + (modify-syntax-entry ?_ "." TeX-mode-syntax-table) + (modify-syntax-entry ?@ "_" TeX-mode-syntax-table) + (modify-syntax-entry ?~ "." TeX-mode-syntax-table) + (modify-syntax-entry ?$ "$" TeX-mode-syntax-table) + (modify-syntax-entry ?' "w" TeX-mode-syntax-table) + (modify-syntax-entry ?« "." TeX-mode-syntax-table) + (modify-syntax-entry ?» "." TeX-mode-syntax-table)) + +;;; Menu Support + +(defvar TeX-command-current 'TeX-command-master + "Specify whether to run command on master, buffer or region.") +;; Function used to run external command. + +(defun TeX-command-select-master () + "Determine that the next command will be on the master file." + (interactive) + (message "Next command will be on the master file.") + (setq TeX-command-current 'TeX-command-master)) + +(defun TeX-command-select-buffer () + "Determine that the next command will be on the buffer." + (interactive) + (message "Next command will be on the buffer") + (setq TeX-command-current 'TeX-command-buffer)) + +(defun TeX-command-select-region () + "Determine that the next command will be on the region." + (interactive) + (message "Next command will be on the region") + (setq TeX-command-current 'TeX-command-region)) + +(defvar TeX-command-force nil) +;; If non-nil, TeX-command-query will return the value of this +;; variable instead of quering the user. + +(defun TeX-command-menu (name) + "Execute `TeX-command-list' NAME from a menu." + (let ((TeX-command-force name)) + (funcall TeX-command-current))) + +(defun TeX-command-menu-print (printer command name) + "Print on PRINTER using method COMMAND to run NAME." + (let ((TeX-printer-default (unless (string= printer "Other") printer)) + (TeX-printer-list (and (string= printer "Other") TeX-printer-list)) + (TeX-print-command command) + (TeX-queue-command command)) + (TeX-command-menu name))) + +(defun TeX-command-menu-printer-entry (entry lookup command name) + "Return `TeX-printer-list' ENTRY as a menu item." + (vector (nth 0 entry) + (list 'TeX-command-menu-print + (nth 0 entry) + (or (nth lookup entry) command) + name))) + +(defun TeX-command-menu-entry (entry) + "Return `TeX-command-list' ENTRY as a menu item." + (let ((name (car entry))) + (cond ((and (string-equal name TeX-command-Print) + TeX-printer-list) + (cons TeX-command-Print + (mapcar (lambda (entry) + (TeX-command-menu-printer-entry + entry 1 TeX-print-command name)) + (append TeX-printer-list '(("Other")))))) + ((and (string-equal name TeX-command-Queue) + TeX-printer-list) + (cons TeX-command-Queue + (mapcar (lambda (entry) + (TeX-command-menu-printer-entry + entry 2 TeX-queue-command name)) + (append TeX-printer-list '(("Other")))))) + (t + (vconcat `(,name (TeX-command-menu ,name)) + (nthcdr 5 entry)))))) + +(defconst TeX-command-menu-name "Command" + "Name to be displayed for the command menu in all modes defined by AUCTeX.") + +;;; Keymap + +(defcustom TeX-electric-escape nil + "If non-nil, ``\\'' will be bound to `TeX-electric-macro'." + :group 'TeX-macro + :type 'boolean) + +(defcustom TeX-electric-sub-and-superscript nil + "If non-nil, insert braces after typing `^' and `_' in math mode." + :group 'TeX-macro + :type 'boolean) + +(defcustom TeX-newline-function 'newline + "Function to be called upon pressing `RET'." + :group 'TeX-indentation + :type '(choice (const newline) + (const newline-and-indent) + (const reindent-then-newline-and-indent) + (sexp :tag "Other"))) + +(defun TeX-insert-backslash (arg) + "Either insert typed key ARG times or call `TeX-electric-macro'. +`TeX-electric-macro' will be called if `TeX-electric-escape' is non-nil." + (interactive "*p") + (if TeX-electric-escape + (TeX-electric-macro) + (self-insert-command arg))) + +(defun TeX-insert-sub-or-superscript (arg) + "Insert typed key ARG times and possibly a pair of braces. +Brace insertion is only done if point is in a math construct and +`TeX-electric-sub-and-superscript' has a non-nil value." + (interactive "*p") + (self-insert-command arg) + (when (and TeX-electric-sub-and-superscript (texmathp)) + (insert (concat TeX-grop TeX-grcl)) + (backward-char))) + +(defun TeX-newline () + "Call the function specified by the variable `TeX-newline-function'." + (interactive) (funcall TeX-newline-function)) + +(defvar TeX-mode-map + (let ((map (make-sparse-keymap))) + ;; Standard + ;; (define-key map "\177" 'backward-delete-char-untabify) + (define-key map "\C-c}" 'up-list) + (define-key map "\C-c#" 'TeX-normal-mode) + (define-key map "\C-c\C-n" 'TeX-normal-mode) + (define-key map "\C-c?" 'TeX-doc) + (define-key map "\C-c\C-i" 'TeX-goto-info-page) + (define-key map "\r" 'TeX-newline) + + ;; From tex.el + (define-key map "\"" 'TeX-insert-quote) + (define-key map "$" 'TeX-insert-dollar) + ;; Removed because LaTeX 2e have a better solution to italic correction. + ;; (define-key map "." 'TeX-insert-punctuation) + ;; (define-key map "," 'TeX-insert-punctuation) + (define-key map "\C-c{" 'TeX-insert-braces) + (define-key map "\C-c\C-f" 'TeX-font) + (define-key map "\C-c\C-m" 'TeX-insert-macro) + (define-key map "\\" 'TeX-insert-backslash) + (define-key map "^" 'TeX-insert-sub-or-superscript) + (define-key map "_" 'TeX-insert-sub-or-superscript) + (define-key map "\e\t" 'TeX-complete-symbol) ;*** Emacs 19 way + + (define-key map "\C-c'" 'TeX-comment-or-uncomment-paragraph) ;*** Old way + (define-key map "\C-c:" 'TeX-comment-or-uncomment-region) ;*** Old way + (define-key map "\C-c\"" 'TeX-uncomment) ;*** Old way + + (define-key map "\C-c;" 'TeX-comment-or-uncomment-region) + (define-key map "\C-c%" 'TeX-comment-or-uncomment-paragraph) + + (define-key map "\C-c\C-t\C-p" 'TeX-PDF-mode) + (define-key map "\C-c\C-t\C-i" 'TeX-interactive-mode) + (define-key map "\C-c\C-t\C-s" 'TeX-source-correlate-mode) + (define-key map "\C-c\C-t\C-r" 'TeX-pin-region) + (define-key map "\C-c\C-w" 'TeX-toggle-debug-bad-boxes); to be removed + (define-key map "\C-c\C-t\C-b" 'TeX-toggle-debug-bad-boxes) + (define-key map "\C-c\C-t\C-w" 'TeX-toggle-debug-warnings) + (define-key map "\C-c\C-v" 'TeX-view) + ;; From tex-buf.el + (define-key map "\C-c\C-d" 'TeX-save-document) + (define-key map "\C-c\C-r" 'TeX-command-region) + (define-key map "\C-c\C-b" 'TeX-command-buffer) + (define-key map "\C-c\C-c" 'TeX-command-master) + (define-key map "\C-c\C-k" 'TeX-kill-job) + (define-key map "\C-c\C-l" 'TeX-recenter-output-buffer) + (define-key map "\C-c^" 'TeX-home-buffer) + (define-key map "\C-c`" 'TeX-next-error) + ;; Remap bindings of `next-error' + (if (featurep 'xemacs) + (substitute-key-definition 'next-error 'TeX-next-error map global-map) + (define-key map [remap next-error] 'TeX-next-error)) + ;; Remap bindings of `previous-error' + (if (featurep 'xemacs) + (substitute-key-definition 'previous-error 'TeX-previous-error + map global-map) + (define-key map [remap previous-error] 'TeX-previous-error)) + ;; From tex-fold.el + (define-key map "\C-c\C-o\C-f" 'TeX-fold-mode) + + ;; Multifile + (define-key map "\C-c_" 'TeX-master-file-ask) ;*** temporary + map) + "Keymap for common TeX and LaTeX commands.") + +(defvar plain-TeX-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map TeX-mode-map) + map) + "Keymap used in plain TeX mode.") + +(defun TeX-mode-specific-command-menu (mode) + "Return a Command menu specific to the major MODE." + ;; COMPATIBILITY for Emacs < 21 + (if (and (not (featurep 'xemacs)) + (= emacs-major-version 20)) + (cons TeX-command-menu-name + (TeX-mode-specific-command-menu-entries mode)) + (list TeX-command-menu-name + :filter `(lambda (&rest ignored) + (TeX-mode-specific-command-menu-entries ',mode)) + "Bug."))) + +(defun TeX-mode-specific-command-menu-entries (mode) + "Return the entries for a Command menu specific to the major MODE." + (append + (TeX-menu-with-help + `("Command on" + [ "Master File" TeX-command-select-master + :keys "C-c C-c" :style radio + :selected (eq TeX-command-current 'TeX-command-master) + :help "Commands in this menu work on the Master File"] + [ "Buffer" TeX-command-select-buffer + :keys "C-c C-b" :style radio + :selected (eq TeX-command-current 'TeX-command-buffer) + :help "Commands in this menu work on the current buffer"] + [ "Region" TeX-command-select-region + :keys "C-c C-r" :style radio + :selected (eq TeX-command-current 'TeX-command-region) + :help "Commands in this menu work on the region"] + [ "Fix the Region" TeX-pin-region + :active (or (if prefix-arg + (<= (prefix-numeric-value prefix-arg) 0) + (and (boundp 'TeX-command-region-begin) + (markerp TeX-command-region-begin))) + (TeX-mark-active)) + ;;:visible (eq TeX-command-current 'TeX-command-region) + :style toggle + :selected (and (boundp 'TeX-command-region-begin) + (markerp TeX-command-region-begin)) + :help "Fix the region for \"Command on Region\""] + "-" + ["Recenter Output Buffer" TeX-recenter-output-buffer + :help "Show the output of current TeX process"] + ["Kill Job" TeX-kill-job + :help "Kill the current TeX process"] + ["Next Error" TeX-next-error + :help "Jump to the next error of the last TeX run"] + ["Quick View" TeX-view + :help "Start a viewer without prompting"] + "-" + ("TeXing Options" + ,@(mapcar (lambda (x) + (let ((symbol (car x)) (name (nth 1 x))) + `[ ,(format "Use %s engine" name) (TeX-engine-set ',symbol) + :style radio :selected (eq TeX-engine ',symbol) + :help ,(format "Use %s engine for compiling" name) ])) + (TeX-engine-alist)) + "-" + [ "Generate PDF" TeX-PDF-mode + :style toggle :selected TeX-PDF-mode + :active (not (eq TeX-engine 'omega)) + :help "Use PDFTeX to generate PDF instead of DVI"] + [ "Run Interactively" TeX-interactive-mode + :style toggle :selected TeX-interactive-mode :keys "C-c C-t C-i" + :help "Stop on errors in a TeX run"] + [ "Correlate I/O" TeX-source-correlate-mode + :style toggle :selected TeX-source-correlate-mode + :help "Enable forward and inverse search in the previewer"] + ["Debug Bad Boxes" TeX-toggle-debug-bad-boxes + :style toggle :selected TeX-debug-bad-boxes :keys "C-c C-t C-b" + :help "Make \"Next Error\" show overfull and underfull boxes"] + ["Debug Warnings" TeX-toggle-debug-warnings + :style toggle :selected TeX-debug-warnings + :help "Make \"Next Error\" show warnings"]))) + (let ((file 'TeX-command-on-current));; is this actually needed? + (TeX-maybe-remove-help + (delq nil + (mapcar 'TeX-command-menu-entry + (TeX-mode-specific-command-list mode))))))) + +(defun TeX-mode-specific-command-list (mode) + "Return the list of commands available in the given MODE." + (let ((full-list TeX-command-list) + out-list + entry) + (while (setq entry (pop full-list)) + ;; `(nth 4 entry)' may be either an atom in case of which the + ;; entry should be present in any mode or a list of major modes. + (if (or (atom (nth 4 entry)) + (memq mode (nth 4 entry))) + (push entry out-list))) + (nreverse out-list))) + +(defvar TeX-fold-menu + (TeX-menu-with-help + '("Show/Hide" + ["Fold Mode" TeX-fold-mode + :style toggle + :selected (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Toggle folding mode"] + "-" + ["Hide All in Current Buffer" TeX-fold-buffer + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Hide all configured TeX constructs in the current buffer"] + ["Hide All in Current Region" TeX-fold-region + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Hide all configured TeX constructs in the marked region"] + ["Hide All in Current Paragraph" TeX-fold-paragraph + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Hide all configured TeX constructs in the paragraph containing point"] + ["Hide Current Macro" TeX-fold-macro + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Hide the macro containing point"] + ["Hide Current Environment" TeX-fold-env + :visible (not (eq major-mode 'plain-tex-mode)) + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Hide the environment containing point"] + ["Hide Current Comment" TeX-fold-comment + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Hide the comment containing point"] + "-" + ["Show All in Current Buffer" TeX-fold-clearout-buffer + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Permanently show all folded content again"] + ["Show All in Current Region" TeX-fold-clearout-region + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Permanently show all folded content in marked region"] + ["Show All in Current Paragraph" TeX-fold-clearout-paragraph + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Permanently show all folded content in paragraph containing point"] + ["Show Current Item" TeX-fold-clearout-item + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Permanently show the item containing point"] + "-" + ["Hide or Show Current Item" TeX-fold-dwim + :active (and (boundp 'TeX-fold-mode) TeX-fold-mode) + :help "Hide or show the item containing point"])) + "Menu definition for commands from tex-fold.el.") + + +;;; Menus for plain TeX mode +(easy-menu-define plain-TeX-mode-command-menu + plain-TeX-mode-map + "Command menu used in TeX mode." + (TeX-mode-specific-command-menu 'plain-tex-mode)) + +(defvar TeX-customization-menu nil) + +(defvar TeX-common-menu-entries + (TeX-menu-with-help + `(("Multifile/Parsing" + ["Switch to Master File" TeX-home-buffer + :help "Switch to buffer of Master File, or buffer of last TeX command"] + ["Save Document" TeX-save-document + :help "Save all buffers associated with the current Master File"] + ["Set Master File" TeX-master-file-ask + :active (not (TeX-local-master-p)) + :help "Set the main file to run TeX commands on"] + ["Reset Buffer" TeX-normal-mode + :help "Save and reparse the current buffer for style information"] + ["Reset AUCTeX" (TeX-normal-mode t) :keys "C-u C-c C-n" + :help "Reset buffer and reload AUCTeX style files"]) + ["Find Documentation..." TeX-doc + :help "Get help on commands, packages, or TeX-related topics in general"] + ["Read the AUCTeX Manual" TeX-goto-info-page + :help "Everything worth reading"] + ("Customize AUCTeX" + ["Browse Options" + (customize-group 'AUCTeX) + :help "Open the customization buffer for AUCTeX"] + ["Extend this Menu" + (progn + (easy-menu-add-item + nil + ;; Ugly hack because docTeX mode uses the LaTeX menu. + (list (if (eq major-mode 'doctex-mode) "LaTeX" TeX-base-mode-name)) + (or TeX-customization-menu + (setq TeX-customization-menu + (customize-menu-create 'AUCTeX "Customize AUCTeX"))))) + :help "Make this menu a full-blown customization menu"]) + ["Report AUCTeX Bug" TeX-submit-bug-report + :help ,(format "Problems with AUCTeX %s? Mail us!" + AUCTeX-version)]))) + +(defvar plain-TeX-menu-entries + (TeX-menu-with-help + `(["Macro..." TeX-insert-macro + :help "Insert a macro and possibly arguments"] + ["Complete" TeX-complete-symbol + :help "Complete the current macro"] + "-" + ("Insert Font" + ["Emphasize" (TeX-font nil ?\C-e) :keys "C-c C-f C-e"] + ["Bold" (TeX-font nil ?\C-b) :keys "C-c C-f C-b"] + ["Typewriter" (TeX-font nil ?\C-t) :keys "C-c C-f C-t"] + ["Small Caps" (TeX-font nil ?\C-c) :keys "C-c C-f C-c"] + ["Sans Serif" (TeX-font nil ?\C-f) :keys "C-c C-f C-f"] + ["Italic" (TeX-font nil ?\C-i) :keys "C-c C-f C-i"] + ["Slanted" (TeX-font nil ?\C-s) :keys "C-c C-f C-s"] + ["Roman" (TeX-font nil ?\C-r) :keys "C-c C-f C-r"] + ["Calligraphic" (TeX-font nil ?\C-a) :keys "C-c C-f C-a"]) + ("Replace Font" + ["Emphasize" (TeX-font t ?\C-e) :keys "C-u C-c C-f C-e"] + ["Bold" (TeX-font t ?\C-b) :keys "C-u C-c C-f C-b"] + ["Typewriter" (TeX-font t ?\C-t) :keys "C-u C-c C-f C-t"] + ["Small Caps" (TeX-font t ?\C-c) :keys "C-u C-c C-f C-c"] + ["Sans Serif" (TeX-font t ?\C-f) :keys "C-u C-c C-f C-f"] + ["Italic" (TeX-font t ?\C-i) :keys "C-u C-c C-f C-i"] + ["Slanted" (TeX-font t ?\C-s) :keys "C-u C-c C-f C-s"] + ["Roman" (TeX-font t ?\C-r) :keys "C-u C-c C-f C-r"] + ["Calligraphic" (TeX-font t ?\C-a) :keys "C-u C-c C-f C-a"]) + ["Delete Font" (TeX-font t ?\C-d) :keys "C-c C-f C-d"] + "-" + ["Comment or Uncomment Region" TeX-comment-or-uncomment-region + :help "Comment or uncomment the currently selected region"] + ["Comment or Uncomment Paragraph" TeX-comment-or-uncomment-paragraph + :help "Comment or uncomment the paragraph containing point"] + ,TeX-fold-menu + "-" . ,TeX-common-menu-entries))) + +(easy-menu-define plain-TeX-mode-menu + plain-TeX-mode-map + "Menu used in plain TeX mode." + (cons "TeX" plain-TeX-menu-entries)) + +;;; AmSTeX + +(defvar AmSTeX-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map TeX-mode-map) + map) + "Keymap used in `AmSTeX-mode'.") + +;; Menu for AmSTeX mode +(easy-menu-define AmSTeX-mode-command-menu + AmSTeX-mode-map + "Command menu used in AmsTeX mode." + (TeX-mode-specific-command-menu 'ams-tex-mode)) + +(easy-menu-define AmSTeX-mode-menu + AmSTeX-mode-map + "Menu used in AMS-TeX mode." + (cons "AmS-TeX" plain-TeX-menu-entries)) + +;;;###autoload +(defun ams-tex-mode () + "Major mode in AUCTeX for editing AmS-TeX files. +See info under AUCTeX for documentation. + +Special commands: +\\{AmSTeX-mode-map} + +Entering AmS-tex-mode calls the value of `text-mode-hook', +then the value of `TeX-mode-hook', and then the value +of `AmS-TeX-mode-hook'." + (interactive) + (plain-TeX-common-initialization) + (setq major-mode 'ams-tex-mode) + (use-local-map AmSTeX-mode-map) + + ;; Menu + (easy-menu-add AmSTeX-mode-menu AmSTeX-mode-map) + (easy-menu-add AmSTeX-mode-command-menu AmSTeX-mode-map) + + (setq TeX-base-mode-name "AmS-TeX") + (setq TeX-command-default "AmSTeX") + (TeX-run-mode-hooks 'text-mode-hook 'TeX-mode-hook 'AmS-TeX-mode-hook) + (TeX-set-mode-name)) + + +;;; Verbatim constructs + +(defvar TeX-verbatim-p-function nil + "Mode-specific function to be called by `TeX-verbatim-p'.") +(make-variable-buffer-local 'TeX-verbatim-p-function) + +;; XXX: We only have an implementation for LaTeX mode at the moment (Oct 2009). +(defun TeX-verbatim-p (&optional pos) + "Return non-nil if position POS is in a verbatim-like construct. +A mode-specific implementation is required. If it is not +available, the function always returns nil." + (when TeX-verbatim-p-function + (funcall TeX-verbatim-p-function))) + + +;;; Comments + +(defvar TeX-comment-start-regexp "%" + "Regular expression matching a comment starter. +Unlike the variable `comment-start-skip' it should not match any +whitespace after the comment starter or any character before it.") +(make-variable-buffer-local 'TeX-comment-start-regexp) + +(defun TeX-comment-region (beg end &optional arg) + "Comment each line in the region from BEG to END. +Numeric prefix arg ARG means use ARG comment characters. +If ARG is negative, delete that many comment characters instead." + (interactive "*r\nP") + ;; `comment-padding' will not be recognized in XEmacs' (21.4) + ;; `comment-region', so we temporarily modify `comment-start' to get + ;; proper spacing. Unfortunately we have to check for the XEmacs + ;; version and cannot test if `comment-padding' is bound as this + ;; gets initialized in `VirTeX-common-initialization'. + (let ((comment-start (if (and (featurep 'xemacs) + (= emacs-major-version 21) + (<= emacs-minor-version 4)) + (concat comment-start (TeX-comment-padding-string)) + comment-start))) + (comment-region beg end arg))) + +(eval-and-compile + ;; COMPATIBILITY for Emacs <= 21.3 + (if (fboundp 'comment-or-uncomment-region) + (defalias 'TeX-comment-or-uncomment-region 'comment-or-uncomment-region) + ;; The following function was copied from `newcomment.el' on + ;; 2004-01-30 and adapted accordingly + (defun TeX-comment-or-uncomment-region (beg end &optional arg) + "Comment or uncomment a the region from BEG to END. +Call `TeX-comment-region', unless the region only consists of +comments, in which case call `TeX-uncomment-region'. If a prefix +arg ARG is given, it is passed on to the respective function." + (interactive "*r\nP") + (funcall (if (save-excursion ;; check for already commented region + (goto-char beg) + (TeX-comment-forward (point-max)) + (<= end (point))) + 'TeX-uncomment-region 'TeX-comment-region) + beg end arg))) + + ;; COMPATIBILITY for Emacs <= 20. (Introduced in 21.1?) + (if (fboundp 'uncomment-region) + (defalias 'TeX-uncomment-region 'uncomment-region) + (defun TeX-uncomment-region (beg end &optional arg) + "Remove comment characters from the beginning of each line +in the region from BEG to END. Numeric prefix arg ARG means use +ARG comment characters. If ARG is negative, delete that many +comment characters instead." + (interactive "*r\nP") + (or arg + ;; Determine the number of comment characters at the + ;; beginning of the first commented line. + (setq arg + (save-excursion + (goto-char beg) + (re-search-forward + (concat "^" TeX-comment-start-regexp "+") end t) + (length (match-string 0))))) + (comment-region beg end (- arg))))) + +(defun TeX-uncomment () + "Delete comment characters from the beginning of each line in a comment." + (interactive) + (save-excursion + ;; Find first comment line + (beginning-of-line) + (while (and (looking-at (concat "^[ \t]*" TeX-comment-start-regexp)) + (not (bobp))) + (forward-line -1)) + (let ((beg (point))) + (forward-line 1) + ;; Find last comment line + (while (and (looking-at (concat "^[ \t]*" TeX-comment-start-regexp)) + (not (eobp))) + (forward-line 1)) + ;; Uncomment region + (TeX-uncomment-region beg (point))))) + +(defun TeX-comment-or-uncomment-paragraph () + "Comment or uncomment current paragraph." + (interactive) + (if (TeX-in-commented-line) + (TeX-uncomment) + (save-excursion + (beginning-of-line) + ;; Don't do anything if we are in an empty line. If this line + ;; is followed by a lot of commented lines, this shall prevent + ;; that mark-paragraph skips over these lines and marks a + ;; paragraph outside the visible window which might get + ;; commented without the user noticing. + (unless (looking-at "^[ \t]*$") + (mark-paragraph) + (TeX-comment-region (point) (mark)))))) + +(defun TeX-in-comment () + "Return non-nil if point is in a comment." + (if (or (bolp) + (null comment-start-skip) + (eq (preceding-char) ?\r)) + nil + (save-excursion + (save-match-data + (let ((pos (point))) + (beginning-of-line) + (and (or (looking-at comment-start-skip) + (re-search-forward comment-start-skip pos t)) + (not (TeX-verbatim-p)))))))) + +(defun TeX-in-commented-line () + "Return non-nil if point is in a line consisting only of a comment. +The comment can be preceded by whitespace. This means that +`TeX-in-commented-line' is more general than `TeX-in-line-comment' +which will not match commented lines with leading whitespace. But +`TeX-in-commented-line' will match commented lines without leading +whitespace as well." + (save-excursion + (forward-line 0) + (skip-chars-forward " \t") + (string= (buffer-substring-no-properties + (point) (min (point-max) (+ (point) (length comment-start)))) + comment-start))) + +(defun TeX-in-line-comment () + "Return non-nil if point is in a line comment. +A line comment is a comment starting in column one, i.e. there is +no whitespace before the comment sign." + (save-excursion + (forward-line 0) + (string= (buffer-substring-no-properties + (point) (min (point-max) (+ (point) (length comment-start)))) + comment-start))) + +(defun TeX-comment-prefix () + "Return the comment prefix of the current line. +If there are no comment starters after potential whitespace at +the beginning of the line, return nil." + (save-excursion + (beginning-of-line) + (save-match-data + (when (looking-at (concat "\\([ \t]*" TeX-comment-start-regexp "+\\)+")) + (match-string 0))))) + +(defun TeX-forward-comment-skip (&optional count limit) + "Move forward to the next comment skip. +This may be a switch between commented and not commented adjacent +lines or between lines with different comment prefixes. With +argument COUNT do it COUNT times. If argument LIMIT is given, do +not move point further than this value." + (unless count (setq count 1)) + ;; A value of 0 is nonsense. + (when (= count 0) (setq count 1)) + (unless limit (setq limit (point-max))) + (dotimes (i (abs count)) + (if (< count 0) + (forward-line -1) + (beginning-of-line)) + (let ((prefix (when (looking-at (concat "\\([ \t]*" + TeX-comment-start-regexp "+\\)+")) + (buffer-substring (+ (line-beginning-position) + (current-indentation)) + (match-end 0))))) + (while (save-excursion + (and (if (> count 0) + (<= (point) limit) + (>= (point) limit)) + (zerop (if (> count 0) + (forward-line 1) + (forward-line -1))) + (if prefix + (if (looking-at (concat "\\([ \t]*" + TeX-comment-start-regexp + "+\\)+")) + ;; If the preceding line is a commented line + ;; as well, check if the prefixes are + ;; identical. + (string= prefix + (buffer-substring + (+ (line-beginning-position) + (current-indentation)) + (match-end 0))) + nil) + (not (looking-at (concat "[ \t]*" + TeX-comment-start-regexp)))))) + (if (> count 0) + (forward-line 1) + (forward-line -1))) + (if (> count 0) + (forward-line 1))))) + +(defun TeX-backward-comment-skip (&optional count limit) + "Move backward to the next comment skip. +This may be a switch between commented and not commented adjacent +lines or between lines with different comment prefixes. With +argument COUNT do it COUNT times. If argument LIMIT is given, do +not move point to a position less than this value." + (unless count (setq count 1)) + (when (= count 0) (setq count 1)) + (unless limit (setq limit (point-min))) + (TeX-forward-comment-skip (- count) limit)) + +;; Taken from `comment-forward' in Emacs' CVS on 2006-12-26. Used as +;; a compatibility function for XEmacs 21.4. +(defun TeX-comment-forward (&optional n) + "Skip forward over N comments. +Just like `forward-comment' but only for positive N +and can use regexps instead of syntax." + (when (fboundp 'comment-normalize-vars) + (comment-normalize-vars)) + (if (fboundp 'comment-forward) + (comment-forward n) + (setq n (or n 1)) + (if (< n 0) (error "No comment-backward") + (if comment-use-syntax (forward-comment n) + (while (> n 0) + (setq n + (if (or (forward-comment 1) + (and (looking-at comment-start-skip) + (goto-char (match-end 0)) + (re-search-forward comment-end-skip nil 'move))) + (1- n) -1))) + (= n 0))))) + +(defun TeX-comment-padding-string () + "Return comment padding as a string. +The variable `comment-padding' can hold an integer or a string. +This function will return the appropriate string representation +regardless of its data type." + (if (integerp comment-padding) + (make-string comment-padding ? ) + comment-padding)) + + +;;; Indentation + +(defgroup TeX-indentation nil + "Indentation of TeX buffers in AUCTeX." + :group 'AUCTeX) + +(defcustom TeX-brace-indent-level 2 + "*The level of indentation produced by an open brace." + :group 'TeX-indentation + :type 'integer) + +(defun TeX-comment-indent () + "Determine the indentation of a comment." + (if (looking-at "%%%") + (current-column) + (skip-chars-backward " \t") + (max (if (bolp) 0 (1+ (current-column))) + comment-column))) + +(defun TeX-brace-count-line () + "Count number of open/closed braces." + (save-excursion + (let ((count 0) (limit (line-end-position)) char) + (while (progn + (skip-chars-forward "^{}\\\\" limit) + (when (and (< (point) limit) (not (TeX-in-comment))) + (setq char (char-after)) + (forward-char) + (cond ((eq char ?\{) + (setq count (+ count TeX-brace-indent-level))) + ((eq char ?\}) + (setq count (- count TeX-brace-indent-level))) + ((eq char ?\\) + (when (< (point) limit) + (forward-char) + t)))))) + count))) + +;;; Navigation + +(defvar TeX-search-syntax-table + (let ((table (make-syntax-table (make-char-table (if (featurep 'xemacs) + 'syntax + 'syntax-table))))) + ;; Preset mode-independent syntax entries. (Mode-dependent + ;; entries are set in the function `TeX-search-syntax-table'.) + ;; ?\", ?\( and ?\) explicitely get whitespace syntax because + ;; Emacs 21.3 and XEmacs don't generate a completely empty syntax + ;; table. + (dolist (elt '((?\f . ">") (?\n . ">") (?\" . " ") (?\( . " ") (?\) . " "))) + (modify-syntax-entry (car elt) (cdr elt) table)) + table) + "Syntax table used for searching purposes. +It should be accessed through the function `TeX-search-syntax-table'.") + +(defun TeX-search-syntax-table (&rest args) + "Return a syntax table for searching purposes. +ARGS may be a list of characters. For each of them the +respective predefined syntax is set. Currently the parenthetical +characters ?{, ?}, ?[, ?], ?\(, ?\), ?<, and ?> are supported. +The syntax of each of these characters not specified will be +reset to \" \"." + (let ((char-syntax-alist '((?\{ . "(}") (?\} . "){") + (?\[ . "(]") (?\] . ")[") + (?\( . "()") (?\) . ")(") + (?\< . "(>") (?\> . ")<")))) + ;; Clean entries possibly set before. + (modify-syntax-entry ?\\ " " TeX-search-syntax-table) + (modify-syntax-entry ?@ " " TeX-search-syntax-table) + (modify-syntax-entry ?\% " " TeX-search-syntax-table) + ;; Preset mode-dependent syntax entries. (Mode-independent entries + ;; are set when the variable `TeX-search-syntax-table' is created.) + (modify-syntax-entry (string-to-char TeX-esc) "\\" TeX-search-syntax-table) + (unless (eq major-mode 'texinfo-mode) + (modify-syntax-entry ?\% "<" TeX-search-syntax-table)) + ;; Clean up the entries which can be specified as arguments. + (dolist (elt char-syntax-alist) + (modify-syntax-entry (car elt) " " TeX-search-syntax-table)) + ;; Now set what we got. + (dolist (elt args) + (unless (assoc elt char-syntax-alist) (error "Char not supported")) + (modify-syntax-entry elt (cdr (assoc elt char-syntax-alist)) + TeX-search-syntax-table)) + ;; Return the syntax table. + TeX-search-syntax-table)) + +(defun TeX-find-balanced-brace (&optional count depth limit) + "Return the position of a balanced brace in a TeX group. +The function scans forward COUNT parenthetical groupings. +Default is 1. If COUNT is negative, it searches backwards. With +optional DEPTH>=1, find that outer level. If LIMIT is non-nil, +do not search further than this position in the buffer." + (let ((count (if count + (if (= count 0) (error "COUNT has to be <> 0") count) + 1)) + (depth (if depth + (if (< depth 1) (error "DEPTH has to be > 0") depth) + 1))) + (save-restriction + (when limit + (if (> count 0) + (narrow-to-region (point-min) limit) + (narrow-to-region limit (point-max)))) + (with-syntax-table (TeX-search-syntax-table ?\{ ?\}) + (condition-case nil + (scan-lists (point) count depth) + (error nil)))))) + +(defun TeX-find-closing-brace (&optional depth limit) + "Return the position of the closing brace in a TeX group. +The function assumes that point is inside the group, i.e. after +an opening brace. With optional DEPTH>=1, find that outer level. +If LIMIT is non-nil, do not search further down than this +position in the buffer." + (TeX-find-balanced-brace 1 depth limit)) + +(defun TeX-find-opening-brace (&optional depth limit) + "Return the position of the opening brace in a TeX group. +The function assumes that point is inside the group, i.e. before +a closing brace. With optional DEPTH>=1, find that outer level. +If LIMIT is non-nil, do not search further up than this position +in the buffer." + (TeX-find-balanced-brace -1 depth limit)) + +(defun TeX-find-macro-boundaries (&optional lower-bound) + "Return a list containing the start and end of a macro. +If LOWER-BOUND is given, do not search backward further than this +point in buffer. Arguments enclosed in brackets or braces are +considered part of the macro." + (save-restriction + (when lower-bound + (narrow-to-region lower-bound (point-max))) + (let ((orig-point (point)) + start-point) + ;; Point is located directly at the start of a macro. (-!-\foo{bar}) + (when (and (eq (char-after) (aref TeX-esc 0)) + (not (TeX-escaped-p))) + (setq start-point (point))) + ;; Point is located on a macro. (\fo-!-o{bar}) + (unless start-point + (save-excursion + (skip-chars-backward "A-Za-z@*") + (when (and (eq (char-before) (aref TeX-esc 0)) + (not (TeX-escaped-p (1- (point))))) + (setq start-point (1- (point)))))) + ;; Point is located in the argument of a macro. (\foo{ba-!-r}) + (unless start-point + (save-excursion + (catch 'abort + (let ((parse-sexp-ignore-comments t)) + (when (condition-case nil (progn (up-list) t) (error nil)) + (while (progn + (condition-case nil (backward-sexp) + (error (throw 'abort nil))) + (forward-comment -1) + (and (memq (char-before) '(?\] ?\})) + (not (TeX-escaped-p (1- (point))))))) + (skip-chars-backward "A-Za-z@*") + (when (and (eq (char-before) (aref TeX-esc 0)) + (not (TeX-escaped-p (1- (point))))) + (setq start-point (1- (point))))))))) + ;; Search forward for the end of the macro. + (when start-point + (save-excursion + (goto-char (TeX-find-macro-end-helper start-point)) + (if (< orig-point (point)) + (cons start-point (point)) + nil)))))) + +(defun TeX-find-macro-end-helper (start) + "Find the end of a macro given its START. +START is the position just before the starting token of the macro. +If the macro is followed by square brackets or curly braces, +those will be considered part of it." + (save-excursion + (save-match-data + (catch 'found + (goto-char (1+ start)) + (if (zerop (skip-chars-forward "A-Za-z@")) + (forward-char) + (skip-chars-forward "*")) + (while (not (eobp)) + (cond + ;; Skip over pairs of square brackets + ((or (looking-at "[ \t]*\n?\\(\\[\\)") ; Be conservative: Consider + ; only consecutive lines. + (and (looking-at (concat "[ \t]*" TeX-comment-start-regexp)) + (save-excursion + (forward-line 1) + (looking-at "[ \t]*\\(\\[\\)")))) + (goto-char (match-beginning 1)) + (condition-case nil + (forward-sexp) + (scan-error (throw 'found (point))))) + ;; Skip over pairs of curly braces + ((or (looking-at "[ \t]*\n?{") ; Be conservative: Consider + ; only consecutive lines. + (and (looking-at (concat "[ \t]*" TeX-comment-start-regexp)) + (save-excursion + (forward-line 1) + (looking-at "[ \t]*{")))) + (goto-char (match-end 0)) + (goto-char (or (TeX-find-closing-brace) + ;; If we cannot find a regular end, use the + ;; next whitespace. + (save-excursion (skip-chars-forward "^ \t\n") + (point)))) + (when (eobp) (throw 'found (point)))) + (t + (throw 'found (point))))))))) + +(defun TeX-find-macro-start (&optional limit) + "Return the start of a macro. +If LIMIT is given, do not search backward further than this point +in buffer. Arguments enclosed in brackets or braces are +considered part of the macro." + (car (TeX-find-macro-boundaries limit))) + +(defun TeX-find-macro-end () + "Return the end of a macro. +Arguments enclosed in brackets or braces are considered part of +the macro." + (cdr (TeX-find-macro-boundaries))) + +(defun TeX-search-forward-unescaped (string &optional bound noerror) + "Search forward from point for unescaped STRING. +The optional argument BOUND limits the search to the respective +buffer position. +If NOERROR is non-nil, return nil if the search failed instead of +throwing an error. +A pattern is escaped, if it is preceded by an odd number of escape +characters." + (TeX-search-unescaped string 'forward nil bound noerror)) + +(defun TeX-search-backward-unescaped (string &optional bound noerror) + "Search backward from point for unescaped STRING. +The optional argument BOUND limits the search to the respective +buffer position. +If NOERROR is non-nil, return nil if the search failed instead of +throwing an error. +A pattern is escaped, if it is preceded by an odd number of escape +characters." + (TeX-search-unescaped string 'backward nil bound noerror)) + +(defun TeX-re-search-forward-unescaped (regexp &optional bound noerror) + "Search forward from point for unescaped regular expression REGEXP. +The optional argument BOUND limits the search to the respective +buffer position. +If NOERROR is non-nil, return nil if the search failed instead of +throwing an error. +A pattern is escaped, if it is preceded by an odd number of escape +characters." + (TeX-search-unescaped regexp 'forward t bound noerror)) + +(defun TeX-search-unescaped (pattern + &optional direction regexp-flag bound noerror) + "Search for unescaped PATTERN in a certain DIRECTION. +DIRECTION can be indicated by the symbols 'forward and 'backward. +If DIRECTION is omitted, a forward search is carried out. +If REGEXP-FLAG is non-nil, PATTERN may be a regular expression, +otherwise a string. +The optional argument BOUND limits the search to the respective +buffer position. +If NOERROR is non-nil, return nil if the search failed instead of +throwing an error. +A pattern is escaped, if it is preceded by an odd number of escape +characters." + (let ((search-fun (if (eq direction 'backward) + (if regexp-flag 're-search-backward 'search-backward) + (if regexp-flag 're-search-forward 'search-forward)))) + (catch 'found + (while (funcall search-fun pattern bound noerror) + (when (not (TeX-escaped-p (match-beginning 0))) + (throw 'found (point))))))) + +(defun TeX-escaped-p (&optional pos) + "Return t if the character at position POS is escaped. +If POS is omitted, examine the character at point. +A character is escaped if it is preceded by an odd number of +escape characters, such as \"\\\" in LaTeX." + (save-excursion + (when pos (goto-char pos)) + (not (zerop (mod (skip-chars-backward (regexp-quote TeX-esc)) 2))))) + +(defun TeX-current-macro () + "Return the name of the macro containing point, nil if there is none." + (let ((macro-start (TeX-find-macro-start))) + (when macro-start + (save-excursion + (goto-char macro-start) + (forward-char (length TeX-esc)) + (buffer-substring-no-properties + (point) (progn (skip-chars-forward "@A-Za-z") (point))))))) + +(defvar TeX-search-forward-comment-start-function nil + "Function to find the start of a comment. +The function should accept an optional argument for specifying +the limit of the search. It should return the position just +before the comment if one is found and nil otherwise. Point +should not be moved.") +(make-variable-buffer-local 'TeX-search-forward-comment-start-function) + +(defun TeX-search-forward-comment-start (&optional limit) + "Search forward for a comment start from current position till LIMIT. +If LIMIT is omitted, search till the end of the buffer. + +The search relies on `TeX-comment-start-regexp' being set +correctly for the current mode. + +Set `TeX-search-forward-comment-start-defun' in order to override +the default implementation." + (if TeX-search-forward-comment-start-function + (funcall TeX-search-forward-comment-start-function limit) + (setq limit (or limit (point-max))) + (when (TeX-re-search-forward-unescaped TeX-comment-start-regexp limit t) + (match-beginning 0)))) + +;;; Fonts + +(defcustom TeX-font-list '((?\C-b "{\\bf " "}") + (?\C-c "{\\sc " "}") + (?\C-e "{\\em " "\\/}") + (?\C-i "{\\it " "\\/}") + (?\C-r "{\\rm " "}") + (?\C-s "{\\sl " "\\/}") + (?\C-t "{\\tt " "}") + (?\C-d "" "" t)) + "List of fonts used by `TeX-font'. + +Each entry is a list. +The first element is the key to activate the font. +The second element is the string to insert before point, and the third +element is the string to insert after point. +If the fourth and fifth element are strings, they specify the prefix and +suffix to be used in math mode. +An optional fourth (or sixth) element means always replace if t." + :group 'TeX-macro + :type '(repeat + (group + :value (?\C-a "" "") + (character :tag "Key") + (string :tag "Prefix") + (string :tag "Suffix") + (option (group + :inline t + (string :tag "Math Prefix") + (string :tag "Math Suffix"))) + (option (sexp :format "Replace\n" :value t))))) + +(defvar TeX-font-replace-function 'TeX-font-replace + "Determines the function which is called when a font should be replaced.") + +(defun TeX-describe-font-entry (entry) + "A textual description of an ENTRY in `TeX-font-list'." + (concat (format "%16s " (key-description (char-to-string (nth 0 entry)))) + (if (or (eq t (nth 3 entry)) (eq t (nth 5 entry))) + "-- delete font" + (format "%14s %-3s %14s %-3s" + (nth 1 entry) (nth 2 entry) + (if (stringp (nth 3 entry)) (nth 3 entry) "") + (if (stringp (nth 4 entry)) (nth 4 entry) ""))))) + +(defun TeX-font (replace what) + "Insert template for font change command. +If REPLACE is not nil, replace current font. WHAT determines the font +to use, as specified by `TeX-font-list'." + (interactive "*P\nc") + (TeX-update-style) + (let* ((entry (assoc what TeX-font-list)) + (in-math (texmathp)) + (before (nth 1 entry)) + (after (nth 2 entry))) + (setq replace (or replace (eq t (nth 3 entry)) (eq t (nth 5 entry)))) + (if (and in-math (stringp (nth 3 entry))) + (setq before (nth 3 entry) + after (nth 4 entry))) + (cond ((null entry) + (let ((help (concat + "Font list: " + "KEY TEXTFONT MATHFONT\n\n" + (mapconcat 'TeX-describe-font-entry + TeX-font-list "\n")))) + (with-output-to-temp-buffer "*Help*" + (set-buffer "*Help*") + (insert help)))) + (replace + (funcall TeX-font-replace-function before after)) + ((TeX-active-mark) + (save-excursion + (cond ((> (mark) (point)) + (insert before) + (goto-char (mark)) + (insert after)) + (t + (insert after) + (goto-char (mark)) + (insert before))))) + (t + (insert before) + (save-excursion + (insert after)))))) + +(defun TeX-font-replace (start end) + "Replace font specification around point with START and END. +For modes with font specifications like `{\\font text}'. +See also `TeX-font-replace-macro' and `TeX-font-replace-function'." + (save-excursion + (while (not (looking-at "{\\\\[a-zA-Z]+ ")) + (up-list -1)) + (forward-sexp) + (save-excursion + (replace-match start t t)) + (if (save-excursion + (backward-char 3) + (if (looking-at (regexp-quote "\\/}")) + (progn + (delete-char 3) + nil) + t)) + (delete-backward-char 1)) + (insert end))) + +(defun TeX-font-replace-macro (start end) + "Replace font specification around point with START and END. +For modes with font specifications like `\\font{text}'. +See also `TeX-font-replace' and `TeX-font-replace-function'." + (let ((font-list TeX-font-list) + cmds strings regexp) + (while font-list + (setq strings (cdr (car font-list)) + font-list (cdr font-list)) + (and (stringp (car strings)) (null (string= (car strings) "")) + (setq cmds (cons (car strings) cmds))) + (setq strings (cdr (cdr strings))) + (and (stringp (car strings)) (null (string= (car strings) "")) + (setq cmds (cons (car strings) cmds)))) + (setq regexp (mapconcat 'regexp-quote cmds "\\|")) + (save-excursion + (catch 'done + (while t + (if (/= ?\\ (following-char)) + (skip-chars-backward "a-zA-Z ")) + (skip-chars-backward (regexp-quote TeX-esc)) + (if (looking-at regexp) + (throw 'done t) + (up-list -1)))) + ;; Use stripped syntax table in order to get stuff like "\emph{(}" right. + (with-syntax-table (TeX-search-syntax-table ?\{ ?\}) + (forward-sexp 2)) + (save-excursion + (replace-match start t t)) + (delete-backward-char 1) + (insert end)))) + +;;; Dollars +;; +;; Rewritten from scratch with use of `texmathp' by +;; Carsten Dominik + +(defvar TeX-symbol-marker nil) + +(defvar TeX-symbol-marker-pos 0) + +;; The following constants are no longer used, but kept in case some +;; foreign code uses any of them. +(defvar TeX-dollar-sign ?$ + "*Character used to enter and leave math mode in TeX.") +(defconst TeX-dollar-string (char-to-string TeX-dollar-sign)) +(defconst TeX-dollar-regexp + (concat "^" (regexp-quote TeX-dollar-string) "\\|[^" TeX-esc "]" + (regexp-quote TeX-dollar-string))) + +(defcustom TeX-math-toggle-off-input-method t + "*If non-nil, auto toggle off CJK input methods when entering math mode." + :group 'TeX-macro + :type 'boolean) + +(defcustom TeX-math-close-double-dollar nil + "If non-nil close double dollar math by typing a single `$'." + :group 'TeX-macro + :type 'boolean) + +(defun TeX-insert-dollar (&optional arg) + "Insert dollar sign. + +If current math mode was not entered with a dollar, refuse to +insert one. Show matching dollar sign if this dollar sign ends +the TeX math mode and `blink-matching-paren' is non-nil. Ensure +double dollar signs match up correctly by inserting extra dollar +signs when needed. + +With raw \\[universal-argument] prefix, insert exactly one dollar +sign. With optional ARG, insert that many dollar signs." + (interactive "P") + (cond + ((and arg (listp arg)) + ;; C-u always inserts one + (insert "$")) + (arg + ;; Numerical arg inserts that many + (insert (make-string (prefix-numeric-value arg) ?\$))) + ((TeX-escaped-p) + ;; This is escaped with `\', so just insert one. + (insert "$")) + ((texmathp) + ;; We are inside math mode + (if (and (stringp (car texmathp-why)) + (string-equal (substring (car texmathp-why) 0 1) "\$")) + ;; Math mode was turned on with $ or $$ - so finish it accordingly. + (progn + (if TeX-math-close-double-dollar + (insert (car texmathp-why)) + (insert "$")) + (when (and blink-matching-paren + (or (string= (car texmathp-why) "$") + (zerop (mod (save-excursion + (skip-chars-backward "$")) 2)))) + (save-excursion + (goto-char (cdr texmathp-why)) + (if (pos-visible-in-window-p) + (sit-for 1) + (message "Matches %s" + (buffer-substring + (point) (progn (end-of-line) (point)))))))) + ;; Math mode was not entered with dollar - we cannot finish it with one. + (message "Math mode started with `%s' cannot be closed with dollar" + (car texmathp-why)) + (insert "$"))) + (t + ;; Just somewhere in the text. + (insert "$"))) + (TeX-math-input-method-off)) + +(defvar TeX-math-input-method-off-regexp + "^\\(chinese\\|japanese\\|korean\\bulgarian\\russian\\)" + "Regexp matching input methods to be deactivated when entering math mode.") + +(defun TeX-math-input-method-off () + "Toggle off input method when entering math mode." + (and TeX-math-toggle-off-input-method + (texmathp) + (boundp 'current-input-method) current-input-method + (string-match TeX-math-input-method-off-regexp current-input-method) + (inactivate-input-method))) + +;;; Simple Commands + +(defun TeX-normal-mode (arg) + "Remove all information about this buffer, and apply the style hooks again. +Save buffer first including style information. +With optional argument ARG, also reload the style hooks." + ;; FIXME: Shouldn't it be (&optional arg)? -- rs + (interactive "*P") + (if arg + (setq TeX-style-hook-list nil + BibTeX-global-style-files nil + BibTeX-global-files nil + TeX-global-input-files nil)) + (let ((TeX-auto-save t)) + (if (buffer-modified-p) + (save-buffer) + (TeX-auto-write))) + (normal-mode) + ;; See also addition to `find-file-hooks' in `VirTeX-common-initialization'. + (when (eq TeX-master 'shared) (TeX-master-file nil nil t)) + (TeX-update-style t)) + +(defgroup TeX-quote nil + "Quoting in AUCTeX." + :group 'AUCTeX) + +(defcustom TeX-open-quote "``" + "String inserted by typing \\[TeX-insert-quote] to open a quotation." + :group 'TeX-quote + :type 'string) + +(defcustom TeX-close-quote "''" + "String inserted by typing \\[TeX-insert-quote] to close a quotation." + :group 'TeX-quote + :type 'string) + +(defcustom TeX-quote-after-quote nil + "Behaviour of \\[TeX-insert-quote]. +Nil means standard behaviour; when non-nil, opening and closing +quotes are inserted only after \"." + :group 'TeX-quote + :type 'boolean) + +(defcustom TeX-quote-language-alist nil + "Alist for overriding the default language-specific quote insertion. +First element in each item is the name of the language as set by +the language style file as a string. Second element is the +opening quotation mark. Third elemxent is the closing quotation +mark. Opening and closing quotation marks can be specified +directly as strings or as functions returning a string. Fourth +element is a boolean specifying insertion behavior, overriding +`TeX-quote-after-quote'. See Info node `(auctex)European' for +valid languages." + :group 'TeX-quote + :link '(custom-manual "(auctex)European") + :type '(repeat (group (choice + (const "czech") + (const "danish") + (const "dutch") + (const "german") + (const "ngerman") + (const "french") ;; not frenchb or francais + (const "italian") + (const "polish") + (const "slovak") + (const "swedish") + (string :tag "Other Language")) + (choice :tag "Opening quotation mark" string function) + (choice :tag "Closing quotation mark" string function) + (boolean :tag "Insert plain quote first" :value t)))) + +(defvar TeX-quote-language nil + "If non-nil determines behavior of quote insertion. +It is usually set by language-related style files. Its value has +the same structure as the elements of `TeX-quote-language-alist'. +The symbol 'override can be used as its car in order to override +the settings of style files. Style files should therefore check +if this symbol is present and not alter `TeX-quote-language' if +it is.") +(make-variable-buffer-local 'TeX-quote-language) + +(defun TeX-insert-quote (force) + "Insert the appropriate quotation marks for TeX. +Inserts the value of `TeX-open-quote' (normally ``) or `TeX-close-quote' +\(normally '') depending on the context. If `TeX-quote-after-quote' +is non-nil, this insertion works only after \". +With prefix argument FORCE, always inserts \" characters." + (interactive "*P") + (if (or force + ;; Do not insert TeX quotes in verbatim, math or comment constructs. + (and (fboundp 'font-latex-faces-present-p) + (font-latex-faces-present-p '(font-latex-verbatim-face + font-latex-math-face + font-lock-comment-face)) + (font-latex-faces-present-p '(font-latex-verbatim-face + font-latex-math-face + font-lock-comment-face) + (1- (point)))) + (texmathp) + (and (TeX-in-comment) (not (eq major-mode 'doctex-mode)))) + (self-insert-command (prefix-numeric-value force)) + (TeX-update-style) + (let* ((lang-override (if (eq (car TeX-quote-language) 'override) + TeX-quote-language + (assoc (car TeX-quote-language) + TeX-quote-language-alist))) + (lang (or lang-override TeX-quote-language)) + (open-quote (if lang (nth 1 lang) TeX-open-quote)) + (close-quote (if lang (nth 2 lang) TeX-close-quote)) + (q-after-q (if lang (nth 3 lang) TeX-quote-after-quote))) + (when (functionp open-quote) + (setq open-quote (funcall open-quote))) + (when (functionp close-quote) + (setq close-quote (funcall close-quote))) + (if q-after-q + (insert (cond ((bobp) + ?\") + ((save-excursion + (TeX-looking-at-backward + (concat (regexp-quote open-quote) "\\|" + (regexp-quote close-quote)) + (max (length open-quote) (length close-quote)))) + (delete-backward-char (length (match-string 0))) + "\"\"") + ((< (save-excursion (skip-chars-backward "\"")) -1) + ?\") + ((not (= (preceding-char) ?\")) + ?\") + ((save-excursion + (forward-char -1) + (bobp)) + (delete-backward-char 1) + open-quote) + ((save-excursion + (forward-char -2) ;;; at -1 there is double quote + (looking-at "[ \t\n]\\|\\s(")) + (delete-backward-char 1) + open-quote) + (t + (delete-backward-char 1) + close-quote))) + (insert (cond ((bobp) + open-quote) + ((= (preceding-char) (string-to-char TeX-esc)) + ?\") + ((= (preceding-char) ?\") + ?\") + ((save-excursion + (forward-char (- (length open-quote))) + (looking-at (regexp-quote open-quote))) + (delete-backward-char (length open-quote)) + ?\") + ((save-excursion + (forward-char (- (length close-quote))) + (looking-at (regexp-quote close-quote))) + (delete-backward-char (length close-quote)) + ?\") + ((save-excursion + (forward-char -1) + (looking-at "[ \t\n]\\|\\s(")) + open-quote) + (t + close-quote))))))) + +(defun TeX-insert-punctuation () + "Insert point or comma, cleaning up preceding space." + (interactive) + (expand-abbrev) + (if (TeX-looking-at-backward "\\\\/\\(}+\\)" 50) + (replace-match "\\1" t)) + (call-interactively 'self-insert-command)) + +(defun TeX-insert-braces (arg) + "Make a pair of braces around next ARG sexps and leave point inside. +No argument is equivalent to zero: just insert braces and leave point +between. + +If there is an active region, ARG will be ignored, braces will be +inserted around the region, and point will be left after the +closing brace." + (interactive "P") + (if (TeX-active-mark) + (progn + (if (< (point) (mark)) + (exchange-point-and-mark)) + (insert TeX-grcl) + (save-excursion + (goto-char (mark)) + (insert TeX-grop))) + (insert TeX-grop) + (save-excursion + (if arg (forward-sexp (prefix-numeric-value arg))) + (insert TeX-grcl)))) + +;;;###autoload +(defun TeX-submit-bug-report () + "Submit a bug report on AUCTeX via mail. + +Don't hesitate to report any problems or inaccurate documentation. + +If you don't have setup sending mail from (X)Emacs, please copy the +output buffer into your mail program, as it gives us important +information about your AUCTeX version and AUCTeX configuration." + (interactive) + (require 'reporter) + (let ((reporter-prompt-for-summary-p "Bug report subject: ")) + (reporter-submit-bug-report + "bug-auctex@gnu.org" + AUCTeX-version + (list 'AUCTeX-date + 'window-system + 'LaTeX-version + 'TeX-style-path + 'TeX-auto-save + 'TeX-parse-self + 'TeX-master + 'TeX-command-list) + nil nil + "Remember to cover the basics, that is, what you expected to happen and +what in fact did happen. + +Be sure to consult the FAQ section in the manual before submitting +a bug report. In addition check if the bug is reproducable with an +up-to-date version of AUCTeX. So please upgrade to the version +available from http://www.gnu.org/software/auctex/ if your +installation is older than the one available from the web site. + +If the bug is triggered by a specific \(La\)TeX file, you should try +to produce a minimal sample file showing the problem and include it +in your report. + +Your bug report will be posted to the AUCTeX bug reporting list. +------------------------------------------------------------------------"))) + + +;;; Documentation + +(defun TeX-goto-info-page () + "Read documentation for AUCTeX in the info system." + (interactive) + (info "auctex")) + +(autoload 'info-lookup->completions "info-look") + +(defvar TeX-doc-backend-alist + '((texdoc (plain-tex-mode latex-mode doctex-mode ams-tex-mode context-mode) + (lambda () + (when (executable-find "texdoc") + (TeX-search-files + ;; Explicitely supply doc directory for + ;; non-kpathsea-based TeX systems. + (unless (stringp TeX-kpathsea-path-delimiter) + (or (TeX-tree-expand + '("$SYSTEXMF" "$TEXMFLOCAL" "$TEXMFMAIN" "$TEXMFDIST") + "latex" '("/doc/")) + `(,@TeX-macro-global ,@TeX-macro-private))) + '("dvi" "pdf" "ps" "txt" "html") t t))) + (lambda (doc) + ;; texdoc in MiKTeX requires --view in order to start + ;; the viewer instead of an intermediate web page. + (call-process "texdoc" nil 0 nil "--view" doc))) + (latex-info (latex-mode) + (lambda () + (when (condition-case nil + (save-window-excursion + (let ((buf (generate-new-buffer-name "*info*"))) + (info "latex" buf) + (kill-buffer buf)) + t) + (error nil)) + (mapcar (lambda (x) + (let ((x (car x))) + (if (string-match "\\`\\\\" x) + (substring x 1) x))) + (info-lookup->completions 'symbol 'latex-mode)))) + (lambda (doc) + (info-lookup-symbol (concat "\\" doc) 'latex-mode))) + (texinfo-info (texinfo-mode) + (lambda () + (when (condition-case nil + (save-window-excursion + (let ((buf (generate-new-buffer-name "*info*"))) + (info "texinfo" buf) + (kill-buffer buf)) + t) + (error nil)) + (mapcar (lambda (x) + (let ((x (car x))) + (if (string-match "\\`@" x) + (substring x 1) x))) + (info-lookup->completions 'symbol + 'texinfo-mode)))) + (lambda (doc) + (info-lookup-symbol (concat "@" doc) 'texinfo-mode)))) + "Alist of backends used for looking up documentation. +Each item consists of four elements. + +The first is a symbol describing the backend's name. + +The second is a list of modes the backend should be activated in. + +The third is a function returning a list of documents available +to the backend. It should return nil if the backend is not +available, e.g. if a required executable is not present on the +system in question. + +The fourth is a function for displaying the documentation. The +function should accept a single argument, the chosen package, +command, or document name.") + +(defun TeX-doc (&optional name) + "Display documentation for string NAME. +NAME may be a package, a command, or a document." + (interactive) + (let (docs) + ;; Build the lists of available documentation used for completion. + (dolist (elt TeX-doc-backend-alist) + (when (memq major-mode (nth 1 elt)) + (let ((completions (funcall (nth 2 elt)))) + (unless (null completions) + (add-to-list 'docs (cons completions (nth 0 elt))))))) + (if (null docs) + (progn + (if (executable-find "texdoc") + ;; Fallback if we did not find anything via the backend list. + (let ((doc (read-from-minibuffer "Input for `texdoc': "))) + (when doc (call-process "texdoc" nil 0 nil "--view" doc))) + ;; Give up. + (message "No documentation found"))) + ;; Ask the user about the package, command, or document. + (when (and (interactive-p) + (or (not name) (string= name ""))) + (let ((symbol (thing-at-point 'symbol)) + contained completions doc) + ;; Is the symbol at point contained in the lists of available + ;; documentation? + (setq contained (catch 'found + (dolist (elt docs) + (when (member symbol (car elt)) + (throw 'found t))))) + ;; Setup completion list in a format suitable for `completing-read'. + (dolist (elt docs) + (setq completions (nconc (mapcar 'list (car elt)) completions))) + ;; Query user. + (setq doc (completing-read + (if contained + (format "Package, command, or document (default %s): " + symbol) + "Package, command, or document: ") + completions)) + (setq name (if (string= doc "") symbol doc)))) + (if (not name) + (message "No documentation specified") + ;; XXX: Provide way to choose in case a symbol can be found in + ;; more than one backend. + (let* ((backend (catch 'found + (dolist (elt docs) + (when (member name (car elt)) + (throw 'found (cdr elt))))))) + (if backend + (funcall (nth 3 (assoc backend TeX-doc-backend-alist)) name) + (message "Documentation not found"))))))) + + +;;; Ispell Support + +;; FIXME: Document those functions and variables. -- rs + +;; The FSF ispell.el use this. +(defun ispell-tex-buffer-p () + (and (boundp 'ispell-tex-p) ispell-tex-p)) + +;; The FSF ispell.el might one day use this. +(setq ispell-enable-tex-parser t) + +(defun TeX-run-ispell (command string file) + "Run ispell on current TeX buffer." + (cond ((and (string-equal file (TeX-region-file)) + (fboundp 'ispell-region)) + (call-interactively 'ispell-region)) + ((string-equal file (TeX-region-file)) + (call-interactively 'spell-region)) + ((fboundp 'ispell-buffer) + (ispell-buffer)) + ((fboundp 'ispell) + (ispell)) + (t + (spell-buffer)))) + +(defun TeX-ispell-document (name) + "Run ispell on all open files belonging to the current document." + (interactive (list (TeX-master-file))) + (if (string-equal name "") + (setq name (TeX-master-file))) + + (let ((found nil) + (regexp (concat "\\`\\(" + (mapconcat (lambda (dir) + (regexp-quote + (expand-file-name + (file-name-as-directory dir)))) + (append (when (file-name-directory name) + (list (file-name-directory name))) + TeX-check-path) + "\\|") + "\\).*\\(" + (mapconcat 'regexp-quote + (cons (file-name-nondirectory name) + (TeX-style-list)) "\\|") + "\\)\\.\\(" + (mapconcat 'regexp-quote TeX-file-extensions "\\|") + "\\)\\'")) + (buffers (buffer-list))) + (while buffers + (let* ((buffer (car buffers)) + (name (buffer-file-name buffer))) + (setq buffers (cdr buffers)) + (if (and name (string-match regexp name)) + (progn + (save-excursion (switch-to-buffer buffer) (ispell-buffer)) + (setq found t))))))) + +;; Some versions of ispell 3 use this. +(defvar ispell-tex-major-modes nil) +(setq ispell-tex-major-modes + (append '(plain-tex-mode ams-tex-mode latex-mode doctex-mode) + ispell-tex-major-modes)) + + +;;; Special provisions for other modes and libraries + +;; desktop-locals-to-save is broken by design. Don't have +;; buffer-local values of it. +(eval-after-load "desktop" + '(progn + (dolist (elt '(TeX-master)) + (unless (member elt (default-value 'desktop-locals-to-save)) + (setq-default desktop-locals-to-save + (cons elt (default-value 'desktop-locals-to-save))))) + (add-hook 'desktop-after-read-hook '(lambda () + (TeX-set-mode-name t))))) + +;; delsel.el, `delete-selection-mode' +(put 'TeX-newline 'delete-selection t) +(put 'TeX-insert-dollar 'delete-selection t) +(put 'TeX-insert-quote 'delete-selection t) +(put 'TeX-insert-backslash 'delete-selection t) + +(provide 'tex) + +;; Local Variables: +;; coding: iso-8859-1 +;; End: + +;;; tex.el ends here