summaryrefslogtreecommitdiff
path: root/lisp/textmodes/tex-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/textmodes/tex-mode.el')
-rw-r--r--lisp/textmodes/tex-mode.el323
1 files changed, 221 insertions, 102 deletions
diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el
index 44bc31d36d6..06e26d21aaf 100644
--- a/lisp/textmodes/tex-mode.el
+++ b/lisp/textmodes/tex-mode.el
@@ -1,7 +1,8 @@
;;; 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 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1989, 1992, 1994, 1995, 1996, 1997, 1998
+;; 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+;; Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: tex
@@ -246,15 +247,20 @@ Normally set to either `plain-tex-mode' or `latex-mode'."
(defcustom tex-fontify-script t
"If non-nil, fontify subscript and superscript strings."
:type 'boolean
- :group 'tex)
+ :group 'tex
+ :version "23.1")
(put 'tex-fontify-script 'safe-local-variable 'booleanp)
(defcustom tex-font-script-display '(-0.2 0.2)
- "Display specification for subscript and superscript content.
-The first is used for subscript, the second is used for superscripts."
+ "How much to lower and raise subscript and superscript content.
+This is a list of two floats. The first is negative and
+specifies how much subscript is lowered, the second is positive
+and specifies how much superscript is raised. Heights are
+measured relative to that of the normal text."
:group 'tex
:type '(list (float :tag "Subscript")
- (float :tag "Superscript")))
+ (float :tag "Superscript"))
+ :version "23.1")
(defvar tex-last-temp-file nil
"Latest temporary file generated by \\[tex-region] and \\[tex-buffer].
@@ -636,7 +642,7 @@ An alternative value is \" . \", if you use a font with a narrow period."
(defvar tex-verbatim-environments
'("verbatim" "verbatim*"))
(put 'tex-verbatim-environments 'safe-local-variable
- (lambda (x) (require 'cl) (every 'stringp x)))
+ (lambda (x) (null (delq t (mapcar 'stringp x)))))
(defvar tex-font-lock-syntactic-keywords
'((eval . `(,(concat "^\\\\begin *{"
@@ -672,12 +678,42 @@ An alternative value is \" . \", if you use a font with a narrow period."
(put-text-property beg next 'display nil))
(setq beg next))))
+(defcustom tex-suscript-height-ratio 0.8
+ "Ratio of subscript/superscript height to that of the preceding text.
+In nested subscript/superscript, this factor is applied repeatedly,
+subject to the limit set by `tex-suscript-height-minimum'."
+ :type 'float
+ :group 'tex
+ :version "23.1")
+
+(defcustom tex-suscript-height-minimum 0.0
+ "Integer or float limiting the minimum size of subscript/superscript text.
+An integer is an absolute height in units of 1/10 point, a float
+is a height relative to that of the default font. Zero means no minimum."
+ :type '(choice (integer :tag "Integer height in 1/10 point units")
+ (float :tag "Fraction of default font height"))
+ :group 'tex
+ :version "23.1")
+
+(defun tex-suscript-height (height)
+ "Return the integer height of subscript/superscript font in 1/10 points.
+Not smaller than the value set by `tex-suscript-height-minimum'."
+ (ceiling (max (if (integerp tex-suscript-height-minimum)
+ tex-suscript-height-minimum
+ ;; For bootstrapping.
+ (condition-case nil
+ (* tex-suscript-height-minimum
+ (face-attribute 'default :height))
+ (error 0)))
+ ;; NB assumes height is integer.
+ (* height tex-suscript-height-ratio))))
+
(defface superscript
- '((t :height 0.8)) ;; :raise 0.2
+ '((t :height tex-suscript-height)) ;; :raise 0.2
"Face used for superscripts."
:group 'tex)
(defface subscript
- '((t :height 0.8)) ;; :raise -0.2
+ '((t :height tex-suscript-height)) ;; :raise -0.2
"Face used for subscripts."
:group 'tex)
@@ -1184,24 +1220,27 @@ on the line for the invalidity you want to see."
(setq occur-revert-arguments (list nil 0 (list buffer))))
(save-excursion
(goto-char (point-max))
- (while (and (not (bobp)))
- (let ((end (point))
- prev-end)
+ ;; Do a little shimmy to place point at the end of the last
+ ;; "real" paragraph. Need to avoid validating across an \end,
+ ;; because that blows up latex-forward-sexp.
+ (backward-paragraph)
+ (forward-paragraph)
+ (while (not (bobp))
;; Scan the previous paragraph for invalidities.
- (if (search-backward "\n\n" nil t)
- (progn
- (setq prev-end (point))
- (forward-char 2))
- (goto-char (setq prev-end (point-min))))
- (or (tex-validate-region (point) end)
- (let* ((end (line-beginning-position 2))
+ (backward-paragraph)
+ (save-excursion
+ (or (tex-validate-region (point) (save-excursion
+ (forward-paragraph)
+ (point)))
+ (let ((end (line-beginning-position 2))
start tem)
(beginning-of-line)
(setq start (point))
;; Keep track of line number as we scan,
;; in a cumulative fashion.
(if linenum
- (setq linenum (- linenum (count-lines prevpos (point))))
+ (setq linenum (- linenum
+ (count-lines prevpos (point))))
(setq linenum (1+ (count-lines 1 start))))
(setq prevpos (point))
;; Mention this mismatch in *Occur*.
@@ -1222,10 +1261,10 @@ on the line for the invalidity you want to see."
(add-text-properties
text-beg (- text-end 1)
'(mouse-face highlight
- help-echo "mouse-2: go to this invalidity"))
+ help-echo
+ "mouse-2: go to this invalidity"))
(put-text-property text-beg (- text-end 1)
- 'occur-target tem)))))
- (goto-char prev-end))))
+ 'occur-target tem))))))))
(with-current-buffer standard-output
(let ((no-matches (zerop num-matches)))
(if no-matches
@@ -1248,7 +1287,9 @@ area if a mismatch is found."
(narrow-to-region start end)
;; First check that the open and close parens balance in numbers.
(goto-char start)
- (while (<= 0 (setq max-possible-sexps (1- max-possible-sexps)))
+ (while (and (not (eobp))
+ (<= 0 (setq max-possible-sexps
+ (1- max-possible-sexps))))
(forward-sexp 1))
;; Now check that like matches like.
(goto-char start)
@@ -1256,6 +1297,7 @@ area if a mismatch is found."
(save-excursion
(let ((pos (match-beginning 0)))
(goto-char pos)
+ (skip-chars-backward "\\\\") ; escaped parens
(forward-sexp 1)
(or (eq (preceding-char) (cdr (syntax-after pos)))
(eq (char-after pos) (cdr (syntax-after (1- (point)))))
@@ -1273,9 +1315,13 @@ A prefix arg inhibits the checking."
(interactive "*P")
(or inhibit-validation
(save-excursion
+ ;; For the purposes of this, a "paragraph" is a block of text
+ ;; wherein all the brackets etc are expected to be balanced. It
+ ;; may start after a blank line (ie a "proper" paragraph), or
+ ;; a begin{} or end{} block, etc.
(tex-validate-region
(save-excursion
- (search-backward "\n\n" nil 'move)
+ (backward-paragraph)
(point))
(point)))
(message "Paragraph being closed appears to contain a mismatch"))
@@ -1383,13 +1429,41 @@ Return the value returned by the last execution of BODY."
(search-failed (error "Couldn't find unended \\begin"))))
(defun tex-next-unmatched-end ()
- "Leave point at the end of the next `\\end' that is unended."
+ "Leave point at the end of the next `\\end' that is unmatched."
(while (and (tex-search-noncomment
(re-search-forward "\\\\\\(begin\\|end\\)\\s *{[^}]+}"))
(save-excursion (goto-char (match-beginning 0))
(looking-at "\\\\begin")))
(tex-next-unmatched-end)))
+(defun tex-next-unmatched-eparen (otype)
+ "Leave point after the next unmatched escaped closing parenthesis.
+The string OTYPE is an opening parenthesis type: `(', `{', or `['."
+ (condition-case nil
+ (let ((ctype (char-to-string (cdr (aref (syntax-table)
+ (string-to-char otype))))))
+ (while (and (tex-search-noncomment
+ (re-search-forward (format "\\\\[%s%s]" ctype otype)))
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (looking-at (format "\\\\%s" (regexp-quote otype)))))
+ (tex-next-unmatched-eparen otype)))
+ (wrong-type-argument (error "Unknown opening parenthesis type: %s" otype))
+ (search-failed (error "Couldn't find closing escaped paren"))))
+
+(defun tex-last-unended-eparen (ctype)
+ "Leave point at the start of the last unended escaped opening parenthesis.
+The string CTYPE is a closing parenthesis type: `)', `}', or `]'."
+ (condition-case nil
+ (let ((otype (char-to-string (cdr (aref (syntax-table)
+ (string-to-char ctype))))))
+ (while (and (tex-search-noncomment
+ (re-search-backward (format "\\\\[%s%s]" ctype otype)))
+ (looking-at (format "\\\\%s" (regexp-quote ctype))))
+ (tex-last-unended-eparen ctype)))
+ (wrong-type-argument (error "Unknown opening parenthesis type: %s" ctype))
+ (search-failed (error "Couldn't find unended escaped paren"))))
+
(defun tex-goto-last-unclosed-latex-block ()
"Move point to the last unclosed \\begin{...}.
Mark is left at original location."
@@ -1401,26 +1475,34 @@ Mark is left at original location."
(push-mark)
(goto-char spot)))
+;; Don't think this one actually _needs_ (for the purposes of
+;; tex-mode) to handle escaped parens.
(defun latex-backward-sexp-1 ()
- "Like (backward-sexp 1) but aware of multi-char elements."
+ "Like (backward-sexp 1) but aware of multi-char elements and escaped parens."
(let ((pos (point))
(forward-sexp-function))
(backward-sexp 1)
- (if (looking-at "\\\\begin\\>")
- (signal 'scan-error
- (list "Containing expression ends prematurely"
- (point) (prog1 (point) (goto-char pos))))
- (when (eq (char-after) ?{)
- (let ((newpos (point)))
- (when (ignore-errors (backward-sexp 1) t)
- (if (or (looking-at "\\\\end\\>")
- ;; In case the \\ ends a verbatim section.
- (and (looking-at "end\\>") (eq (char-before) ?\\)))
- (tex-last-unended-begin)
- (goto-char newpos))))))))
-
+ (cond ((looking-at "\\\\\\(begin\\>\\|[[({]\\)")
+ (signal 'scan-error
+ (list "Containing expression ends prematurely"
+ (point) (prog1 (point) (goto-char pos)))))
+ ((looking-at "\\\\\\([])}]\\)")
+ (tex-last-unended-eparen (match-string 1)))
+ ((eq (char-after) ?{)
+ (let ((newpos (point)))
+ (when (ignore-errors (backward-sexp 1) t)
+ (if (or (looking-at "\\\\end\\>")
+ ;; In case the \\ ends a verbatim section.
+ (and (looking-at "end\\>") (eq (char-before) ?\\)))
+ (tex-last-unended-begin)
+ (goto-char newpos))))))))
+
+;; Note this does not handle things like mismatched brackets inside
+;; begin/end blocks.
+;; Needs to handle escaped parens for tex-validate-*.
+;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-09/msg00038.html
(defun latex-forward-sexp-1 ()
- "Like (forward-sexp 1) but aware of multi-char elements."
+ "Like (forward-sexp 1) but aware of multi-char elements and escaped parens."
(let ((pos (point))
(forward-sexp-function))
(forward-sexp 1)
@@ -1437,10 +1519,19 @@ Mark is left at original location."
((looking-at "\\\\begin\\>")
(goto-char (match-end 0))
(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 "\\\\[])}]")
+ (signal 'scan-error
+ (list "Containing expression ends prematurely"
+ (- (point) 2) (prog1 (point)
+ (goto-char pos)))))
+ ((looking-back "\\\\\\([({[]\\)")
+ (tex-next-unmatched-eparen (match-string 1)))
(t (goto-char newpos))))))
(defun latex-forward-sexp (&optional arg)
- "Like `forward-sexp' but aware of multi-char elements."
+ "Like `forward-sexp' but aware of multi-char elements and escaped parens."
(interactive "P")
(unless arg (setq arg 1))
(let ((pos (point)))
@@ -2032,29 +2123,37 @@ for the error messages."
(file-name-directory (buffer-file-name tex-last-buffer-texed)))
found-desired (num-errors-found 0)
last-filename last-linenum last-position
- begin-of-error end-of-error)
+ begin-of-error end-of-error errfilename)
;; Don't reparse messages already seen at last parse.
(goto-char compilation-parsing-end)
;; Parse messages.
(while (and (not (or found-desired (eobp)))
- (prog1 (re-search-forward "^! " nil 'move)
+ ;; First alternative handles the newer --file-line-error style:
+ ;; ./test2.tex:14: Too many }'s.
+ ;; Second handles the old-style:
+ ;; ! Too many }'s.
+ (prog1 (re-search-forward
+ "^\\(?:\\([^:\n]+\\):[[:digit:]]+:\\|!\\) " nil 'move)
(setq begin-of-error (match-beginning 0)
- end-of-error (match-end 0)))
+ end-of-error (match-end 0)
+ errfilename (match-string 1)))
(re-search-forward
"^l\\.\\([0-9]+\\) \\(\\.\\.\\.\\)?\\(.*\\)$" nil 'move))
(let* ((this-error (copy-marker begin-of-error))
(linenum (string-to-number (match-string 1)))
(error-text (regexp-quote (match-string 3)))
(filename
- (save-excursion
- (with-syntax-table tex-error-parse-syntax-table
- (backward-up-list 1)
- (skip-syntax-forward "(_")
- (while (not (file-readable-p (thing-at-point 'filename)))
- (skip-syntax-backward "(_")
- (backward-up-list 1)
- (skip-syntax-forward "(_"))
- (thing-at-point 'filename))))
+ ;; Prefer --file-liner-error filename if we have it.
+ (or errfilename
+ (save-excursion
+ (with-syntax-table tex-error-parse-syntax-table
+ (backward-up-list 1)
+ (skip-syntax-forward "(_")
+ (while (not (file-readable-p (thing-at-point 'filename)))
+ (skip-syntax-backward "(_")
+ (backward-up-list 1)
+ (skip-syntax-forward "(_"))
+ (thing-at-point 'filename)))))
(new-file
(or (null last-filename)
(not (string-equal last-filename filename))))
@@ -2124,57 +2223,31 @@ The value of `tex-command' specifies the command to use to run TeX."
(let* ((zap-directory
(file-name-as-directory (expand-file-name tex-directory)))
(tex-out-file (expand-file-name (concat tex-zap-file ".tex")
- zap-directory)))
+ zap-directory))
+ (main-file (expand-file-name (tex-main-file)))
+ (ismain (string-equal main-file (buffer-file-name)))
+ already-output)
;; Don't delete temp files if we do the same buffer twice in a row.
(or (eq (current-buffer) tex-last-buffer-texed)
(tex-delete-last-temp-files t))
- ;; Write the new temp file.
- (save-excursion
- (save-restriction
- (widen)
- (goto-char (point-min))
- (forward-line 100)
- (let ((search-end (point))
- (default-directory zap-directory)
- (already-output 0))
- (goto-char (point-min))
-
- ;; Maybe copy first line, such as `\input texinfo', to temp file.
- (and tex-first-line-header-regexp
- (looking-at tex-first-line-header-regexp)
- (write-region (point)
- (progn (forward-line 1)
- (setq already-output (point)))
- tex-out-file nil nil))
-
- ;; Write out the header, if there is one,
- ;; and any of the specified region which extends before it.
- ;; But don't repeat anything already written.
- (if (re-search-forward tex-start-of-header search-end t)
- (let (hbeg)
- (beginning-of-line)
- (setq hbeg (point)) ;mark beginning of header
- (if (re-search-forward tex-end-of-header nil t)
- (let (hend)
- (forward-line 1)
- (setq hend (point)) ;mark end of header
- (write-region (max (min hbeg beg) already-output)
- hend
- tex-out-file
- (not (zerop already-output)) nil)
- (setq already-output hend)))))
-
- ;; Write out the specified region
- ;; (but don't repeat anything already written).
- (write-region (max beg already-output) end
- tex-out-file
- (not (zerop already-output)) nil))
- ;; Write the trailer, if any.
- ;; Precede it with a newline to make sure it
- ;; is not hidden in a comment.
- (if tex-trailer
- (write-region (concat "\n" tex-trailer) nil
- tex-out-file t nil))))
+ (let ((default-directory zap-directory)) ; why?
+ ;; We assume the header is fully contained in tex-main-file.
+ ;; We use f-f-ns so we get prompted about any changes on disk.
+ (with-current-buffer (find-file-noselect main-file)
+ (setq already-output (tex-region-header tex-out-file
+ (and ismain beg))))
+ ;; Write out the specified region (but don't repeat anything
+ ;; already written in the header).
+ (write-region (if ismain
+ (max beg already-output)
+ beg)
+ end tex-out-file (not (zerop already-output)))
+ ;; Write the trailer, if any.
+ ;; Precede it with a newline to make sure it
+ ;; is not hidden in a comment.
+ (if tex-trailer
+ (write-region (concat "\n" tex-trailer) nil
+ tex-out-file t)))
;; Record the file name to be deleted afterward.
(setq tex-last-temp-file tex-out-file)
;; Use a relative file name here because (1) the proper dir
@@ -2183,6 +2256,52 @@ The value of `tex-command' specifies the command to use to run TeX."
(tex-start-tex tex-command (concat tex-zap-file ".tex") zap-directory)
(setq tex-print-file tex-out-file)))
+(defun tex-region-header (file &optional beg)
+ "If there is a TeX header in the current buffer, write it to FILE.
+Return point at the end of the region so written, or zero. If
+the optional buffer position BEG is specified, then the region
+written out starts at BEG, if this lies before the start of the header.
+
+If the first line matches `tex-first-line-header-regexp', it is
+also written out. The variables `tex-start-of-header' and
+`tex-end-of-header' are used to locate the header. Note that the
+start of the header is required to be within the first 100 lines."
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (let ((search-end (save-excursion
+ (forward-line 100)
+ (point)))
+ (already-output 0)
+ hbeg hend)
+ ;; Maybe copy first line, such as `\input texinfo', to temp file.
+ (and tex-first-line-header-regexp
+ (looking-at tex-first-line-header-regexp)
+ (write-region (point)
+ (progn (forward-line 1)
+ (setq already-output (point)))
+ file))
+ ;; Write out the header, if there is one, and any of the
+ ;; specified region which extends before it. But don't repeat
+ ;; anything already written.
+ (and tex-start-of-header
+ (re-search-forward tex-start-of-header search-end t)
+ (progn
+ (beginning-of-line)
+ (setq hbeg (point)) ; mark beginning of header
+ (when (re-search-forward tex-end-of-header nil t)
+ (forward-line 1)
+ (setq hend (point)) ; mark end of header
+ (write-region
+ (max (if beg
+ (min hbeg beg)
+ hbeg)
+ already-output)
+ hend file (not (zerop already-output)))
+ (setq already-output hend))))
+ already-output))))
+
(defun tex-buffer ()
"Run TeX on current buffer. See \\[tex-region] for more information.
Does not save the buffer, so it's useful for trying experimental versions.