;;; tex-mode.el --- TeX, LaTeX, and SliTeX mode commands -*- coding: utf-8 -*-
;; Copyright (C) 1985, 1986, 1989, 1992, 1994, 1995, 1996, 1997, 1998
-;; 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+;; 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; Maintainer: FSF
;;;###autoload
(defcustom tex-shell-file-name nil
- "*If non-nil, the shell file name to run in the subshell used to run TeX."
+ "If non-nil, the shell file name to run in the subshell used to run TeX."
:type '(choice (const :tag "None" nil)
string)
:group 'tex-run)
;;;###autoload
-(defcustom tex-directory "."
- "*Directory in which temporary files are written.
+(defcustom tex-directory (purecopy ".")
+ "Directory in which temporary files are written.
You can make this `/tmp' if your TEXINPUTS has no relative directories in it
and you don't try to apply \\[tex-region] or \\[tex-buffer] when there are
`\\input' commands with relative directories."
;;;###autoload
(defcustom tex-main-file nil
- "*The main TeX source file which includes this buffer's file.
+ "The main TeX source file which includes this buffer's file.
The command `tex-file' runs TeX on the file specified by `tex-main-file'
if the variable is non-nil."
:type '(choice (const :tag "None" nil)
;;;###autoload
(defcustom tex-offer-save t
- "*If non-nil, ask about saving modified buffers before \\[tex-file] is run."
+ "If non-nil, ask about saving modified buffers before \\[tex-file] is run."
:type 'boolean
:group 'tex-file)
;;;###autoload
-(defcustom tex-run-command "tex"
- "*Command used to run TeX subjob.
+(defcustom tex-run-command (purecopy "tex")
+ "Command used to run TeX subjob.
TeX Mode sets `tex-command' to this string.
See the documentation of that variable."
:type 'string
:group 'tex-run)
;;;###autoload
-(defcustom latex-run-command "latex"
- "*Command used to run LaTeX subjob.
+(defcustom latex-run-command (purecopy "latex")
+ "Command used to run LaTeX subjob.
LaTeX Mode sets `tex-command' to this string.
See the documentation of that variable."
:type 'string
:group 'tex-run)
;;;###autoload
-(defcustom slitex-run-command "slitex"
- "*Command used to run SliTeX subjob.
+(defcustom slitex-run-command (purecopy "slitex")
+ "Command used to run SliTeX subjob.
SliTeX Mode sets `tex-command' to this string.
See the documentation of that variable."
:type 'string
:group 'tex-run)
;;;###autoload
-(defcustom tex-start-options ""
- "*TeX options to use when starting TeX.
+(defcustom tex-start-options (purecopy "")
+ "TeX options to use when starting TeX.
These immediately precede the commands in `tex-start-commands'
and the input file name, with no separating space and are not shell-quoted.
If nil, TeX runs with no options. See the documentation of `tex-command'."
:version "22.1")
;;;###autoload
-(defcustom tex-start-commands "\\nonstopmode\\input"
- "*TeX commands to use when starting TeX.
+(defcustom tex-start-commands (purecopy "\\nonstopmode\\input")
+ "TeX commands to use when starting TeX.
They are shell-quoted and precede the input file name, with a separating space.
If nil, no commands are used. See the documentation of `tex-command'."
:type '(radio (const :tag "Interactive \(nil\)" nil)
;;;###autoload
(defcustom latex-block-names nil
- "*User defined LaTeX block names.
+ "User defined LaTeX block names.
Combined with `latex-standard-block-names' for minibuffer completion."
:type '(repeat string)
:group 'tex-run)
;;;###autoload
-(defcustom tex-bibtex-command "bibtex"
- "*Command used by `tex-bibtex-file' to gather bibliographic data.
+(defcustom tex-bibtex-command (purecopy "bibtex")
+ "Command used by `tex-bibtex-file' to gather bibliographic data.
If this string contains an asterisk (`*'), that is replaced by the file name;
otherwise, the file name, preceded by blank, is added at the end."
:type 'string
:group 'tex-run)
;;;###autoload
-(defcustom tex-dvi-print-command "lpr -d"
- "*Command used by \\[tex-print] to print a .dvi file.
+(defcustom tex-dvi-print-command (purecopy "lpr -d")
+ "Command used by \\[tex-print] to print a .dvi file.
If this string contains an asterisk (`*'), that is replaced by the file name;
otherwise, the file name, preceded by blank, is added at the end."
:type 'string
:group 'tex-view)
;;;###autoload
-(defcustom tex-alt-dvi-print-command "lpr -d"
- "*Command used by \\[tex-print] with a prefix arg to print a .dvi file.
+(defcustom tex-alt-dvi-print-command (purecopy "lpr -d")
+ "Command used by \\[tex-print] with a prefix arg to print a .dvi file.
If this string contains an asterisk (`*'), that is replaced by the file name;
otherwise, the file name, preceded by blank, is added at the end.
;;;###autoload
(defcustom tex-dvi-view-command
- '(cond
- ((eq window-system 'x) "xdvi")
- ((eq window-system 'w32) "yap")
- (t "dvi2tty * | cat -s"))
- "*Command used by \\[tex-view] to display a `.dvi' file.
+ `(cond
+ ((eq window-system 'x) ,(purecopy "xdvi"))
+ ((eq window-system 'w32) ,(purecopy "yap"))
+ (t ,(purecopy "dvi2tty * | cat -s")))
+ "Command used by \\[tex-view] to display a `.dvi' file.
If it is a string, that specifies the command directly.
If this string contains an asterisk (`*'), that is replaced by the file name;
otherwise, the file name, preceded by a space, is added at the end.
:group 'tex-view)
;;;###autoload
-(defcustom tex-show-queue-command "lpq"
- "*Command used by \\[tex-show-print-queue] to show the print queue.
+(defcustom tex-show-queue-command (purecopy "lpq")
+ "Command used by \\[tex-show-print-queue] to show the print queue.
Should show the queue(s) that \\[tex-print] puts jobs on."
:type 'string
:group 'tex-view)
;;;###autoload
(defcustom tex-default-mode 'latex-mode
- "*Mode to enter for a new file that might be either TeX or LaTeX.
+ "Mode to enter for a new file that might be either TeX or LaTeX.
This variable is used when it can't be determined whether the file
is plain TeX or LaTeX or what because the file contains no commands.
Normally set to either `plain-tex-mode' or `latex-mode'."
:group 'tex)
;;;###autoload
-(defcustom tex-open-quote "``"
- "*String inserted by typing \\[tex-insert-quote] to open a quotation."
+(defcustom tex-open-quote (purecopy "``")
+ "String inserted by typing \\[tex-insert-quote] to open a quotation."
:type 'string
:options '("``" "\"<" "\"`" "<<" "«")
:group 'tex)
;;;###autoload
-(defcustom tex-close-quote "''"
- "*String inserted by typing \\[tex-insert-quote] to close a quotation."
+(defcustom tex-close-quote (purecopy "''")
+ "String inserted by typing \\[tex-insert-quote] to close a quotation."
:type 'string
:options '("''" "\">" "\"'" ">>" "»")
:group 'tex)
;;;;
(defcustom latex-imenu-indent-string ". "
- "*String to add repeated in front of nested sectional units for Imenu.
+ "String to add repeated in front of nested sectional units for Imenu.
An alternative value is \" . \", if you use a font with a narrow period."
:type 'string
:group 'tex)
;; (arg "\\(?:{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)\\|\\\\[a-z*]+\\)"))
(arg "{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)"))
(list
- ;; font-lock-syntactic-keywords causes the \ of \end{verbatim} to be
- ;; highlighted as tex-verbatim face. Let's undo that.
- ;; This is ugly and brittle :-( --Stef
- '("^\\(\\\\\\)end" (1 (get-text-property (match-end 1) 'face) t))
;; display $$ math $$
;; We only mark the match between $$ and $$ because the $$ delimiters
;; themselves have already been marked (along with $..$) by syntactic
(list "\\$\\$\\([^$]+\\)\\$\\$" 1 'tex-math-face)
;; Heading args.
(list (concat slash headings "\\*?" opt arg)
- ;; If ARG ends up matching too much (if the {} don't match, f.ex)
+ ;; If ARG ends up matching too much (if the {} don't match, e.g.)
;; jit-lock will do funny things: when updating the buffer
;; the re-highlighting is only done locally so it will just
;; match the local line, but defer-contextually will
(put 'tex-verbatim-environments 'safe-local-variable
(lambda (x) (null (delq t (mapcar 'stringp x)))))
-(defvar tex-font-lock-syntactic-keywords
- '((eval . `(,(concat "^\\\\begin *{"
- (regexp-opt tex-verbatim-environments t)
- "}.*\\(\n\\)") 2 "|"))
- ;; Technically, we'd like to put the "|" property on the \n preceding
- ;; the \end, but this would have 2 disadvantages:
- ;; 1 - it's wrong if the verbatim env is empty (the same \n is used to
- ;; start and end the fenced-string).
- ;; 2 - font-lock considers the preceding \n as being part of the
- ;; preceding line, so things gets screwed every time the previous
- ;; line is re-font-locked on its own.
- ;; There's a hack in tex-font-lock-keywords-1 to remove the verbatim
- ;; face from the \ but C-M-f still jumps to the wrong spot :-( --Stef
- (eval . `(,(concat "^\\(\\\\\\)end *{"
- (regexp-opt tex-verbatim-environments t)
- "}\\(.?\\)") (1 "|") (3 "<")))
- ;; ("^\\(\\\\\\)begin *{comment}" 1 "< b")
- ;; ("^\\\\end *{comment}.*\\(\n\\)" 1 "> b")
+(eval-when-compile
+ (defconst tex-syntax-propertize-rules
+ (syntax-propertize-precompile-rules
("\\\\verb\\**\\([^a-z@*]\\)"
- ;; Do it last, because it uses syntax-ppss which needs the
- ;; syntax-table properties of previous entries.
- 1 (tex-font-lock-verb (match-end 1)))))
+ (1 (prog1 "\""
+ (tex-font-lock-verb
+ (match-beginning 0) (char-after (match-beginning 1))))))))
+
+ (defconst latex-syntax-propertize-rules
+ (syntax-propertize-precompile-rules
+ tex-syntax-propertize-rules
+ ("\\\\\\(?:end\\|begin\\) *\\({[^\n{}]*}\\)"
+ (1 (ignore
+ (tex-env-mark (match-beginning 0)
+ (match-beginning 1) (match-end 1))))))))
+
+(defun tex-env-mark (cmd start end)
+ (when (= cmd (line-beginning-position))
+ (let ((arg (buffer-substring-no-properties (1+ start) (1- end))))
+ (when (member arg tex-verbatim-environments)
+ (if (eq ?b (char-after (1+ cmd)))
+ ;; \begin
+ (put-text-property (line-end-position)
+ (line-beginning-position 2)
+ 'syntax-table (string-to-syntax "< c"))
+ ;; In the case of an empty verbatim env, the \n after the \begin is
+ ;; the same as the \n before the \end. Lucky for us, the "> c"
+ ;; property associated to the \end will be placed afterwards, so it
+ ;; will override the "< c".
+ (put-text-property (1- cmd) cmd
+ 'syntax-table (string-to-syntax "> c"))
+ ;; The text between \end{verbatim} and \n is ignored, so we'll treat
+ ;; it as a comment.
+ (put-text-property end (min (1+ end) (line-end-position))
+ 'syntax-table (string-to-syntax "<"))))))
+ ;; Mark env args for possible electric pairing.
+ (unless (get-char-property (1+ start) 'text-clones) ;Already paired-up.
+ (put-text-property start end 'latex-env-pair t)))
+
+(define-minor-mode latex-electric-env-pair-mode
+ "Automatically update the \\end arg when editing the \\begin one.
+And vice-versa."
+ :lighter "/e"
+ (if latex-electric-env-pair-mode
+ (add-hook 'before-change-functions
+ #'latex-env-before-change nil 'local)
+ (remove-hook 'before-change-functions
+ #'latex-env-before-change 'local)))
+
+(defun latex-env-before-change (start end)
+ (when (get-text-property start 'latex-env-pair)
+ (condition-case err
+ (with-silent-modifications
+ ;; Remove properties even if don't find a pair.
+ (remove-text-properties
+ (previous-single-property-change (1+ start) 'latex-env-pair)
+ (next-single-property-change start 'latex-env-pair)
+ '(latex-env-pair))
+ (unless (or (get-char-property start 'text-clones)
+ (get-char-property (1+ start) 'text-clones)
+ (save-excursion
+ (goto-char start)
+ (not (re-search-backward
+ "\\\\\\(?:end\\|begi\\(n\\)\\) *{"
+ (line-beginning-position) t))))
+ (let ((cmd-start (match-beginning 0))
+ (type (match-end 1)) ;nil for \end, else \begin.
+ (arg-start (1- (match-end 0))))
+ (save-excursion
+ (goto-char (match-end 0))
+ (when (and (looking-at "[^\n{}]*}")
+ (> (match-end 0) end))
+ (let ((arg-end (match-end 0)))
+ (if (null type) ;\end
+ (progn (goto-char arg-end)
+ (latex-forward-sexp -1) (forward-word 1))
+ (goto-char cmd-start)
+ (latex-forward-sexp 1)
+ (let (forward-sexp-function) (backward-sexp)))
+ (when (looking-at
+ (regexp-quote (buffer-substring arg-start arg-end)))
+ (text-clone-create arg-start arg-end))))))))
+ (scan-error nil)
+ (error (message "Error in latex-env-before-change: %s" err)))))
(defun tex-font-lock-unfontify-region (beg end)
(font-lock-default-unfontify-region beg end)
'((t :inherit font-lock-string-face))
"Face used to highlight TeX math expressions."
:group 'tex)
-;; backward-compatibility alias
-(put 'tex-math-face 'face-alias 'tex-math)
+(define-obsolete-face-alias 'tex-math-face 'tex-math "22.1")
(defvar tex-math-face 'tex-math)
(defface tex-verbatim
;; '((t :inherit font-lock-string-face))
- '((t :family "monospace"))
+ '((t :family "courier"))
"Face used to highlight TeX verbatim environments."
:group 'tex)
-;; backward-compatibility alias
-(put 'tex-verbatim-face 'face-alias 'tex-verbatim)
+(define-obsolete-face-alias 'tex-verbatim-face 'tex-verbatim "22.1")
(defvar tex-verbatim-face 'tex-verbatim)
-(defun tex-font-lock-verb (end)
- "Place syntax-table properties on the \verb construct.
-END is the position of the first delimiter after \verb."
- (unless (nth 8 (syntax-ppss end))
+(defun tex-font-lock-verb (start delim)
+ "Place syntax table properties on the \verb construct.
+START is the position of the \\ and DELIM is the delimiter char."
;; Do nothing if the \verb construct is itself inside a comment or
;; verbatim env.
- (save-excursion
+ (unless (nth 8 (save-excursion (syntax-ppss start)))
;; Let's find the end and mark it.
- ;; We used to do it inside tex-font-lock-syntactic-face-function, but
- ;; this leads to funny effects when jumping to the end of the buffer,
- ;; because font-lock applies font-lock-syntactic-keywords to the whole
- ;; preceding text but font-lock-syntactic-face-function only to the
- ;; actually displayed text.
- (goto-char end)
- (let ((char (char-before)))
- (skip-chars-forward (string ?^ char)) ;; Use `end' ?
- (when (eq (char-syntax (preceding-char)) ?/)
- (put-text-property (1- (point)) (point) 'syntax-table '(1)))
+ ;; This may span more than a single line, but we don't bother
+ ;; placing a syntax-multiline property since such multiline verbs aren't
+ ;; valid anyway.
+ (skip-chars-forward (string ?^ delim))
(unless (eobp)
- (put-text-property (point) (1+ (point)) 'syntax-table '(7))
- ;; Cause the rest of the buffer to be re-fontified.
- ;; (remove-text-properties (1+ (point)) (point-max) '(fontified))
- )))
- "\""))
+ (when (eq (char-syntax (preceding-char)) ?/)
+ (put-text-property (1- (point)) (point)
+ 'syntax-table (string-to-syntax ".")))
+ (put-text-property (point) (1+ (point))
+ 'syntax-table (string-to-syntax "\"")))))
;; Use string syntax but math face for $...$.
(defun tex-font-lock-syntactic-face-function (state)
(let ((char (nth 3 state)))
(cond
- ((not char) font-lock-comment-face)
+ ((not char)
+ (if (eq 2 (nth 7 state)) tex-verbatim-face font-lock-comment-face))
((eq char ?$) tex-math-face)
+ ;; A \verb element.
(t tex-verbatim-face))))
\f
(define-key map "\C-c\C-c" 'tex-compile)
(define-key map "\C-c\C-i" 'tex-bibtex-file)
(define-key map "\C-c\C-o" 'latex-insert-block)
+
+ ;; Redundant keybindings, for consistency with SGML mode.
+ (define-key map "\C-c\C-t" 'latex-insert-block)
+ (define-key map "\C-c]" 'latex-close-block)
+ (define-key map "\C-c/" 'latex-close-block)
+
(define-key map "\C-c\C-e" 'latex-close-block)
(define-key map "\C-c\C-u" 'tex-goto-last-unclosed-latex-block)
(define-key map "\C-c\C-m" 'tex-feed-input)
,@tex-face-alist)
"Alist of face and LaTeX font name for facemenu.")
+(defun tex-facemenu-add-face-function (face end)
+ (or (cdr (assq face tex-face-alist))
+ (or (and (consp face)
+ (consp (car face))
+ (null (cdr face))
+ (eq major-mode 'latex-mode)
+ ;; This actually requires the `color' LaTeX package.
+ (cond ((eq (caar face) :foreground)
+ (format "{\\color{%s} " (cadr (car face))))
+ ((eq (caar face) :background)
+ (format "\\colorbox{%s}{" (cadr (car face))))))
+ (error "Face %s not configured for %s mode" face mode-name))))
+
;; This would be a lot simpler if we just used a regexp search,
;; but then it would be too slow.
(defun tex-guess-mode ()
;; remaining warning from byte-compiling all of Emacs...
(eval-when-compile
(setq byte-compile-function-environment
- (delq (assq 'tex-mode byte-compile-function-environment)
- byte-compile-function-environment)))
+ (delq (assq 'tex-mode byte-compile-function-environment)
+ byte-compile-function-environment)))
;;;###autoload
(defun tex-mode ()
"\\>\\|\\\\[a-z]*" (regexp-opt '("space" "skip" "page") t)
"\\>\\)"))
(setq paragraph-separate
- (concat "[\f]\\|[ \t]*\\($\\|"
+ (concat "[\f%]\\|[ \t]*\\($\\|"
"\\\\[][]\\|"
"\\\\" (regexp-opt (append
(mapcar 'car latex-section-alist)
(add-hook 'fill-nobreak-predicate 'latex-fill-nobreak-predicate nil t)
(set (make-local-variable 'indent-line-function) 'latex-indent)
(set (make-local-variable 'fill-indent-according-to-mode) t)
+ (add-hook 'completion-at-point-functions
+ 'latex-complete-data nil 'local)
(set (make-local-variable 'outline-regexp) latex-outline-regexp)
(set (make-local-variable 'outline-level) 'latex-outline-level)
(set (make-local-variable 'forward-sexp-function) 'latex-forward-sexp)
(set (make-local-variable 'compare-windows-whitespace)
'tex-categorize-whitespace)
(set (make-local-variable 'facemenu-add-face-function)
- (lambda (face end)
- (or (cdr (assq face tex-face-alist))
- (error "Face %s not configured for %s mode" face mode-name))))
+ 'tex-facemenu-add-face-function)
(set (make-local-variable 'facemenu-end-add-face) "}")
(set (make-local-variable 'facemenu-remove-face-function) t)
(set (make-local-variable 'font-lock-defaults)
(font-lock-syntactic-face-function
. tex-font-lock-syntactic-face-function)
(font-lock-unfontify-region-function
- . tex-font-lock-unfontify-region)
- (font-lock-syntactic-keywords
- . tex-font-lock-syntactic-keywords)
- (parse-sexp-lookup-properties . t)))
+ . tex-font-lock-unfontify-region)))
+ (set (make-local-variable 'syntax-propertize-function)
+ (syntax-propertize-rules latex-syntax-propertize-rules))
;; TABs in verbatim environments don't do what you think.
(set (make-local-variable 'indent-tabs-mode) nil)
;; Other vars that should be buffer-local.
(let ((no-matches (zerop num-matches)))
(if no-matches
(insert "None!\n"))
- (if (interactive-p)
+ (if (called-interactively-p 'interactive)
(message (cond (no-matches "No mismatches found")
((= num-matches 1) "1 mismatch found")
(t "%d mismatches found"))
\n "\\item " >)
\f
+;;;; LaTeX completion.
+
+(defvar latex-complete-bibtex-cache nil)
+
+(defun latex-string-prefix-p (str1 str2)
+ (eq t (compare-strings str1 nil nil str2 0 (length str1))))
+
+(defvar bibtex-reference-key)
+(declare-function reftex-get-bibfile-list "reftex-cite.el" ())
+
+(defun latex-complete-bibtex-keys ()
+ (when (bound-and-true-p reftex-mode)
+ (lambda (key pred action)
+ (let ((re (concat "^[ \t]*@\\([a-zA-Z]+\\)[ \t\n]*\\([{(][ \t\n]*\\)"
+ (regexp-quote key)))
+ (files (reftex-get-bibfile-list))
+ keys)
+ (if (and (eq (car latex-complete-bibtex-cache)
+ (reftex-get-bibfile-list))
+ (latex-string-prefix-p (nth 1 latex-complete-bibtex-cache)
+ key))
+ ;; Use the cache.
+ (setq keys (nth 2 latex-complete-bibtex-cache))
+ (dolist (file files)
+ (with-current-buffer (find-file-noselect file)
+ (goto-char (point-min))
+ (while (re-search-forward re nil t)
+ (goto-char (match-end 2))
+ (when (and (not (member-ignore-case (match-string 1)
+ '("c" "comment" "string")))
+ (looking-at bibtex-reference-key))
+ (push (match-string-no-properties 0) keys)))))
+ ;; Fill the cache.
+ (set (make-local-variable 'latex-complete-bibtex-cache)
+ (list files key keys)))
+ (complete-with-action action keys key pred)))))
+
+(defun latex-complete-envnames ()
+ (append latex-block-names latex-standard-block-names))
+
+(defun latex-complete-refkeys ()
+ (when (boundp 'reftex-docstruct-symbol)
+ (symbol-value reftex-docstruct-symbol)))
+
+(defvar latex-complete-alist
+ ;; TODO: Add \begin, \end, \ref, ...
+ '(("\\`\\\\\\(short\\)?cite\\'" . latex-complete-bibtex-keys)
+ ("\\`\\\\\\(begin\\|end\\)\\'" . latex-complete-envnames)
+ ("\\`\\\\[vf]?ref\\'" . latex-complete-refkeys)))
+
+(defun latex-complete-data ()
+ "Get completion-data at point."
+ (save-excursion
+ (let ((pt (point)))
+ (skip-chars-backward "^ {}\n\t\\\\")
+ (case (char-before)
+ ((nil ?\s ?\n ?\t ?\}) nil)
+ (?\\
+ ;; TODO: Complete commands.
+ nil)
+ (?\{
+ ;; Complete args to commands.
+ (let* ((cmd
+ (save-excursion
+ (forward-char -1)
+ (skip-chars-backward " \n")
+ (buffer-substring (point)
+ (progn
+ (skip-chars-backward "a-zA-Z@*")
+ (let ((n (skip-chars-backward "\\\\")))
+ (forward-char (* 2 (/ n 2))))
+ (point)))))
+ (start (point))
+ (_ (progn (goto-char pt) (skip-chars-backward "^," start)))
+ (comp-beg (point))
+ (_ (progn (goto-char pt) (skip-chars-forward "^, {}\n\t\\\\")))
+ (comp-end (point))
+ (table
+ (funcall
+ (let ((f (lambda () t)))
+ (dolist (comp latex-complete-alist)
+ (if (string-match (car comp) cmd)
+ (setq f (cdr comp))))
+ f))))
+ (if (eq table t)
+ ;; Unknown command.
+ nil
+ (list comp-beg comp-end table))))))))
+
;;;;
;;;; LaTeX syntax navigation
;;;;
(push-mark)
(goto-char spot)))
+(defvar latex-handle-escaped-parens t)
+
;; Don't think this one actually _needs_ (for the purposes of
;; tex-mode) to handle escaped parens.
+;; Does not handle escaped parens when latex-handle-escaped-parens is nil.
(defun latex-backward-sexp-1 ()
"Like (backward-sexp 1) but aware of multi-char elements and escaped parens."
(let ((pos (point))
(forward-sexp-function))
(backward-sexp 1)
- (cond ((looking-at "\\\\\\(begin\\>\\|[[({]\\)")
+ (cond ((looking-at
+ (if latex-handle-escaped-parens
+ "\\\\\\(begin\\>\\|[[({]\\)"
+ "\\\\begin\\>"))
(signal 'scan-error
(list "Containing expression ends prematurely"
(point) (prog1 (point) (goto-char pos)))))
- ((looking-at "\\\\\\([])}]\\)")
+ ((and latex-handle-escaped-parens
+ (looking-at "\\\\\\([])}]\\)"))
(tex-last-unended-eparen (match-string 1)))
((eq (char-after) ?{)
(let ((newpos (point)))
;; begin/end blocks.
;; Needs to handle escaped parens for tex-validate-*.
;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00038.html
+;; Does not handle escaped parens when latex-handle-escaped-parens is nil.
(defun latex-forward-sexp-1 ()
"Like (forward-sexp 1) but aware of multi-char elements and escaped parens."
(let ((pos (point))
(tex-next-unmatched-end))
;; A better way to handle this, \( .. \) etc, is probably to
;; temporarily change the syntax of the \ in \( to punctuation.
- ((looking-back "\\\\[])}]")
+ ((and latex-handle-escaped-parens
+ (looking-back "\\\\[])}]"))
(signal 'scan-error
(list "Containing expression ends prematurely"
(- (point) 2) (prog1 (point)
(goto-char pos)))))
- ((looking-back "\\\\\\([({[]\\)")
+ ((and latex-handle-escaped-parens
+ (looking-back "\\\\\\([({[]\\)"))
(tex-next-unmatched-eparen (match-string 1)))
(t (goto-char newpos))))))
(setq directory (file-name-as-directory (expand-file-name directory)))
(if (not (file-directory-p directory))
(error "%s is not a directory" directory)
- (save-excursion
- (set-buffer buffer)
+ (with-current-buffer buffer
(setq default-directory directory))))
(defvar tex-send-command-modified-tick 0)
" (.*)\\.")
nil t)
(> (save-excursion
- (or (re-search-backward "\\[[0-9]+\\]" nil t)
+ ;; Usually page numbers are output as [N], but
+ ;; I've already seen things like
+ ;; [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}]
+ (or (re-search-backward "\\[[0-9]+\\({[^}]*}\\)?\\]"
+ nil t)
(point-min)))
(save-excursion
(or (re-search-backward "Rerun" nil t)
(with-syntax-table tex-error-parse-syntax-table
(backward-up-list 1)
(skip-syntax-forward "(_")
- (while (not
+ (while (not
(and (setq try-filename (thing-at-point
'filename))
(not (string= "" try-filename))
(find-file-noselect filename))
(save-excursion
(if new-file
- (progn (goto-line linenum) (setq last-position nil))
+ (progn
+ (goto-char (point-min))
+ (forward-line (1- linenum))
+ (setq last-position nil))
(goto-char last-position)
(forward-line (- linenum last-linenum)))
;; first try a forward search for the error text,
(if (tex-shell-running)
(tex-kill-job)
(tex-start-shell))
- (let (shell-dirtrack-verbose
- (tex-out-file
- (tex-append (file-name-nondirectory (buffer-file-name)) ""))
- (file-dir (file-name-directory (buffer-file-name))))
+ (let* (shell-dirtrack-verbose
+ (source-file (expand-file-name (tex-main-file)))
+ (tex-out-file
+ (tex-append (file-name-nondirectory source-file) ""))
+ (file-dir (file-name-directory source-file)))
(tex-send-command tex-shell-cd-command file-dir)
(tex-send-command tex-bibtex-command tex-out-file))
(tex-display-shell))
(indent-line-to indent)
(save-excursion (indent-line-to indent)))))))
+(defcustom latex-indent-within-escaped-parens nil
+ "Non-nil means add extra indent to text within escaped parens.
+When this is non-nil, text within matching pairs of escaped
+parens is indented at the column following the open paren. The
+default value does not add any extra indent thus providing the
+behavior of Emacs 22 and earlier."
+ :type 'boolean
+ :group 'tex
+ :version "23.1")
+
(defun latex-find-indent (&optional virtual)
"Find the proper indentation of text after point.
VIRTUAL if non-nil indicates that we're only trying to find the indentation
in order to determine the indentation of something else.
There might be text before point."
- (save-excursion
- (skip-chars-forward " \t")
- (or
- ;; Stick the first line at column 0.
- (and (= (point-min) (line-beginning-position)) 0)
- ;; Trust the current indentation, if such info is applicable.
- (and virtual (save-excursion (skip-chars-backward " \t&") (bolp))
- (current-column))
- ;; Stick verbatim environments to the left margin.
- (and (looking-at "\\\\\\(begin\\|end\\) *{\\([^\n}]+\\)")
- (member (match-string 2) tex-verbatim-environments)
- 0)
- ;; Put leading close-paren where the matching open brace would be.
- (and (eq (latex-syntax-after) ?\))
- (ignore-errors
- (save-excursion
- (latex-skip-close-parens)
- (latex-backward-sexp-1)
- (latex-find-indent 'virtual))))
- ;; Default (maybe an argument)
- (let ((pos (point))
- ;; Outdent \item if necessary.
- (indent (if (looking-at tex-indent-item-re) (- tex-indent-item) 0))
- up-list-pos)
- ;; Find the previous point which determines our current indentation.
- (condition-case err
- (progn
- (latex-backward-sexp-1)
- (while (> (current-column) (current-indentation))
- (latex-backward-sexp-1)))
- (scan-error
- (setq up-list-pos (nth 2 err))))
- (cond
- ((= (point-min) pos) 0) ; We're really just indenting the first line.
- ((integerp up-list-pos)
- ;; Have to indent relative to the open-paren.
- (goto-char up-list-pos)
- (if (and (not tex-indent-allhanging)
- (save-excursion
- ;; Make sure we're an argument to a macro and
- ;; that the macro is at the beginning of a line.
- (condition-case nil
- (progn
- (while (eq (char-syntax (char-after)) ?\()
- (forward-sexp -1))
- (and (eq (char-syntax (char-after)) ?/)
- (progn (skip-chars-backward " \t&")
- (bolp))))
- (scan-error nil)))
- (> pos (progn (latex-down-list)
- (forward-comment (point-max))
- (point))))
- ;; Align with the first element after the open-paren.
- (current-column)
- ;; We're the first element after a hanging brace.
+ (let ((latex-handle-escaped-parens latex-indent-within-escaped-parens))
+ (save-excursion
+ (skip-chars-forward " \t")
+ (or
+ ;; Stick the first line at column 0.
+ (and (= (point-min) (line-beginning-position)) 0)
+ ;; Trust the current indentation, if such info is applicable.
+ (and virtual (save-excursion (skip-chars-backward " \t&") (bolp))
+ (current-column))
+ ;; Stick verbatim environments to the left margin.
+ (and (looking-at "\\\\\\(begin\\|end\\) *{\\([^\n}]+\\)")
+ (member (match-string 2) tex-verbatim-environments)
+ 0)
+ ;; Put leading close-paren where the matching open paren would be.
+ (let (escaped)
+ (and (or (eq (latex-syntax-after) ?\))
+ ;; Try to handle escaped close parens but keep
+ ;; original position if it doesn't work out.
+ (and latex-handle-escaped-parens
+ (setq escaped (looking-at "\\\\\\([])}]\\)"))))
+ (ignore-errors
+ (save-excursion
+ (when escaped
+ (goto-char (match-beginning 1)))
+ (latex-skip-close-parens)
+ (latex-backward-sexp-1)
+ (latex-find-indent 'virtual)))))
+ ;; Default (maybe an argument)
+ (let ((pos (point))
+ ;; Outdent \item if necessary.
+ (indent (if (looking-at tex-indent-item-re) (- tex-indent-item) 0))
+ up-list-pos)
+ ;; Find the previous point which determines our current indentation.
+ (condition-case err
+ (progn
+ (latex-backward-sexp-1)
+ (while (> (current-column) (current-indentation))
+ (latex-backward-sexp-1)))
+ (scan-error
+ (setq up-list-pos (nth 2 err))))
+ (cond
+ ((= (point-min) pos) 0) ; We're really just indenting the first line.
+ ((integerp up-list-pos)
+ ;; Have to indent relative to the open-paren.
(goto-char up-list-pos)
- (+ (if (and (looking-at "\\\\begin *{\\([^\n}]+\\)")
- (member (match-string 1)
- latex-noindent-environments))
- 0 tex-indent-basic)
- indent (latex-find-indent 'virtual))))
- ;; We're now at the "beginning" of a line.
- ((not (and (not virtual) (eq (char-after) ?\\)))
- ;; Nothing particular here: just keep the same indentation.
- (+ indent (current-column)))
- ;; We're now looking at a macro call.
- ((looking-at tex-indent-item-re)
- ;; Indenting relative to an item, have to re-add the outdenting.
- (+ indent (current-column) tex-indent-item))
- (t
- (let ((col (current-column)))
- (if (or (not (eq (char-syntax (or (char-after pos) ?\s)) ?\())
- ;; Can't be an arg if there's an empty line inbetween.
- (save-excursion (re-search-forward "^[ \t]*$" pos t)))
- ;; If the first char was not an open-paren, there's
- ;; a risk that this is really not an argument to the
- ;; macro at all.
- (+ indent col)
- (forward-sexp 1)
- (if (< (line-end-position)
- (save-excursion (forward-comment (point-max))
- (point)))
- ;; we're indenting the first argument.
- (min (current-column) (+ tex-indent-arg col))
- (skip-syntax-forward " ")
- (current-column))))))))))
+ (if (and (not tex-indent-allhanging)
+ (save-excursion
+ ;; Make sure we're an argument to a macro and
+ ;; that the macro is at the beginning of a line.
+ (condition-case nil
+ (progn
+ (while (eq (char-syntax (char-after)) ?\()
+ (forward-sexp -1))
+ (and (eq (char-syntax (char-after)) ?/)
+ (progn (skip-chars-backward " \t&")
+ (bolp))))
+ (scan-error nil)))
+ (> pos (progn (latex-down-list)
+ (forward-comment (point-max))
+ (point))))
+ ;; Align with the first element after the open-paren.
+ (current-column)
+ ;; We're the first element after a hanging brace.
+ (goto-char up-list-pos)
+ (+ (if (and (looking-at "\\\\begin *{\\([^\n}]+\\)")
+ (member (match-string 1)
+ latex-noindent-environments))
+ 0 tex-indent-basic)
+ indent (latex-find-indent 'virtual))))
+ ;; We're now at the "beginning" of a line.
+ ((not (and (not virtual) (eq (char-after) ?\\)))
+ ;; Nothing particular here: just keep the same indentation.
+ (+ indent (current-column)))
+ ;; We're now looking at a macro call.
+ ((looking-at tex-indent-item-re)
+ ;; Indenting relative to an item, have to re-add the outdenting.
+ (+ indent (current-column) tex-indent-item))
+ (t
+ (let ((col (current-column)))
+ (if (or (not (eq (char-syntax (or (char-after pos) ?\s)) ?\())
+ ;; Can't be an arg if there's an empty line inbetween.
+ (save-excursion (re-search-forward "^[ \t]*$" pos t)))
+ ;; If the first char was not an open-paren, there's
+ ;; a risk that this is really not an argument to the
+ ;; macro at all.
+ (+ indent col)
+ (forward-sexp 1)
+ (if (< (line-end-position)
+ (save-excursion (forward-comment (point-max))
+ (point)))
+ ;; we're indenting the first argument.
+ (min (current-column) (+ tex-indent-arg col))
+ (skip-syntax-forward " ")
+ (current-column)))))))))))
;;; DocTeX support
(defun doctex-font-lock-^^A ()
;; syntax-table can't deal with. We could turn it
;; into a non-comment, or use `\n%' or `%^' as the comment.
;; Instead, we include it in the ^^A comment.
- (eval-when-compile (string-to-syntax "< b"))
- (eval-when-compile (string-to-syntax ">"))))
+ (string-to-syntax "< b")
+ (string-to-syntax ">")))
(let ((end (line-end-position)))
(if (< end (point-max))
(put-text-property
end (1+ end)
'syntax-table
- (eval-when-compile (string-to-syntax "> b")))))
- (eval-when-compile (string-to-syntax "< b")))))
+ (string-to-syntax "> b"))))
+ (string-to-syntax "< b"))))
(defun doctex-font-lock-syntactic-face-function (state)
;; Mark DocTeX documentation, which is parsed as a style A comment
(tex-font-lock-syntactic-face-function state)
font-lock-doc-face))
-(defvar doctex-font-lock-syntactic-keywords
- (append
- tex-font-lock-syntactic-keywords
- ;; For DocTeX comment-in-doc.
- `(("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A))))))
+(eval-when-compile
+ (defconst doctex-syntax-propertize-rules
+ (syntax-propertize-precompile-rules
+ latex-syntax-propertize-rules
+ ;; For DocTeX comment-in-doc.
+ ("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A))))))
(defvar doctex-font-lock-keywords
(append tex-font-lock-keywords
(mapcar
(lambda (x)
(case (car-safe x)
- (font-lock-syntactic-keywords
- (cons (car x) 'doctex-font-lock-syntactic-keywords))
(font-lock-syntactic-face-function
(cons (car x) 'doctex-font-lock-syntactic-face-function))
(t x)))
- (cdr font-lock-defaults)))))
+ (cdr font-lock-defaults))))
+ (set (make-local-variable 'syntax-propertize-function)
+ (syntax-propertize-rules doctex-syntax-propertize-rules)))
(run-hooks 'tex-mode-load-hook)