diff options
Diffstat (limited to 'lisp/textmodes/tex-mode.el')
-rw-r--r-- | lisp/textmodes/tex-mode.el | 323 |
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. |