1 |
;;; tex-mode.el --- TeX, LaTeX, and SliTeX mode commands -*- coding: utf-8 -*- |
;;; tex-mode.el --- TeX, LaTeX, and SliTeX mode commands -*- coding: utf-8 -*- |
2 |
|
|
3 |
;; Copyright (C) 1985, 86, 89, 92, 94, 95, 96, 97, 98, 1999, 2002 |
;; Copyright (C) 1985,86,89,92,94,95,96,97,98,1999,2002,2003 |
4 |
;; Free Software Foundation, Inc. |
;; Free Software Foundation, Inc. |
5 |
|
|
6 |
;; Maintainer: FSF |
;; Maintainer: FSF |
196 |
:group 'tex-view) |
:group 'tex-view) |
197 |
|
|
198 |
;;;###autoload |
;;;###autoload |
199 |
(defcustom tex-dvi-view-command '(if (eq window-system 'x) \"xdvi\" \"dvi2tty * | cat -s\") |
(defcustom tex-dvi-view-command '(if (eq window-system 'x) "xdvi" "dvi2tty * | cat -s") |
200 |
"*Command used by \\[tex-view] to display a `.dvi' file. |
"*Command used by \\[tex-view] to display a `.dvi' file. |
201 |
If it is a string, that specifies the command directly. |
If it is a string, that specifies the command directly. |
202 |
If this string contains an asterisk (`*'), that is replaced by the file name; |
If this string contains an asterisk (`*'), that is replaced by the file name; |
276 |
"File name that \\[tex-print] prints. |
"File name that \\[tex-print] prints. |
277 |
Set by \\[tex-region], \\[tex-buffer], and \\[tex-file].") |
Set by \\[tex-region], \\[tex-buffer], and \\[tex-file].") |
278 |
|
|
279 |
(easy-mmode-defsyntax tex-mode-syntax-table |
(defvar tex-mode-syntax-table |
280 |
'((?% . "<") |
(let ((st (make-syntax-table))) |
281 |
(?\n . ">") |
(modify-syntax-entry ?% "<" st) |
282 |
(?\f . ">") |
(modify-syntax-entry ?\n ">" st) |
283 |
(?\C-@ . "w") |
(modify-syntax-entry ?\f ">" st) |
284 |
(?' . "w") |
(modify-syntax-entry ?\C-@ "w" st) |
285 |
(?@ . "_") |
(modify-syntax-entry ?' "w" st) |
286 |
(?* . "_") |
(modify-syntax-entry ?@ "_" st) |
287 |
(?\t . " ") |
(modify-syntax-entry ?* "_" st) |
288 |
(?~ . " ") |
(modify-syntax-entry ?\t " " st) |
289 |
(?$ . "$$") |
;; ~ is printed by TeX as a space, but it's semantics in the syntax |
290 |
(?\\ . "/") |
;; of TeX is not `whitespace' (i.e. it's just like \hspace{foo}). |
291 |
(?\" . ".") |
(modify-syntax-entry ?~ "." st) |
292 |
(?& . ".") |
(modify-syntax-entry ?$ "$$" st) |
293 |
(?_ . ".") |
(modify-syntax-entry ?\\ "/" st) |
294 |
(?^ . ".")) |
(modify-syntax-entry ?\" "." st) |
295 |
|
(modify-syntax-entry ?& "." st) |
296 |
|
(modify-syntax-entry ?_ "." st) |
297 |
|
(modify-syntax-entry ?^ "." st) |
298 |
|
st) |
299 |
"Syntax table used while in TeX mode.") |
"Syntax table used while in TeX mode.") |
300 |
|
|
301 |
;;;; |
;;;; |
503 |
(bold (regexp-opt '("textbf" "textsc" "textup" |
(bold (regexp-opt '("textbf" "textsc" "textup" |
504 |
"boldsymbol" "pmb") t)) |
"boldsymbol" "pmb") t)) |
505 |
(italic (regexp-opt '("textit" "textsl" "emph") t)) |
(italic (regexp-opt '("textit" "textsl" "emph") t)) |
506 |
(type (regexp-opt '("texttt" "textmd" "textrm" "textsf") t)) |
;; FIXME: unimplemented yet. |
507 |
|
;; (type (regexp-opt '("texttt" "textmd" "textrm" "textsf") t)) |
508 |
;; |
;; |
509 |
;; Names of commands whose arg should be fontified as a citation. |
;; Names of commands whose arg should be fontified as a citation. |
510 |
(citations (regexp-opt |
(citations (regexp-opt |
695 |
(define-key map "\C-c\C-r" 'tex-region) |
(define-key map "\C-c\C-r" 'tex-region) |
696 |
(define-key map "\C-c\C-b" 'tex-buffer) |
(define-key map "\C-c\C-b" 'tex-buffer) |
697 |
(define-key map "\C-c\C-f" 'tex-file) |
(define-key map "\C-c\C-f" 'tex-file) |
698 |
|
(define-key map "\C-c\C-c" 'tex-compile) |
699 |
(define-key map "\C-c\C-i" 'tex-bibtex-file) |
(define-key map "\C-c\C-i" 'tex-bibtex-file) |
700 |
(define-key map "\C-c\C-o" 'latex-insert-block) |
(define-key map "\C-c\C-o" 'latex-insert-block) |
701 |
(define-key map "\C-c\C-e" 'latex-close-block) |
(define-key map "\C-c\C-e" 'latex-close-block) |
719 |
(defvar latex-mode-map |
(defvar latex-mode-map |
720 |
(let ((map (make-sparse-keymap))) |
(let ((map (make-sparse-keymap))) |
721 |
(set-keymap-parent map tex-mode-map) |
(set-keymap-parent map tex-mode-map) |
722 |
|
(define-key map "\C-c\C-s" 'latex-split-block) |
723 |
map) |
map) |
724 |
"Keymap for `latex-mode'. See also `tex-mode-map'.") |
"Keymap for `latex-mode'. See also `tex-mode-map'.") |
725 |
|
|
752 |
|
|
753 |
;; This would be a lot simpler if we just used a regexp search, |
;; This would be a lot simpler if we just used a regexp search, |
754 |
;; but then it would be too slow. |
;; but then it would be too slow. |
755 |
;;;###autoload |
(defun tex-guess-mode () |
|
(defun tex-mode () |
|
|
"Major mode for editing files of input for TeX, LaTeX, or SliTeX. |
|
|
Tries to determine (by looking at the beginning of the file) whether |
|
|
this file is for plain TeX, LaTeX, or SliTeX and calls `plain-tex-mode', |
|
|
`latex-mode', or `slitex-mode', respectively. If it cannot be determined, |
|
|
such as if there are no commands in the file, the value of `tex-default-mode' |
|
|
says which mode to use." |
|
|
(interactive) |
|
756 |
(let ((mode tex-default-mode) slash comment) |
(let ((mode tex-default-mode) slash comment) |
757 |
(save-excursion |
(save-excursion |
758 |
(goto-char (point-min)) |
(goto-char (point-min)) |
768 |
(concat |
(concat |
769 |
(regexp-opt '("documentstyle" "documentclass" |
(regexp-opt '("documentstyle" "documentclass" |
770 |
"begin" "subsection" "section" |
"begin" "subsection" "section" |
771 |
"part" "chapter" "newcommand") 'words) |
"part" "chapter" "newcommand" |
772 |
|
"renewcommand") 'words) |
773 |
"\\|NeedsTeXFormat{LaTeX"))) |
"\\|NeedsTeXFormat{LaTeX"))) |
774 |
(if (looking-at |
(if (looking-at |
775 |
"document\\(style\\|class\\)\\(\\[.*\\]\\)?{slides}") |
"document\\(style\\|class\\)\\(\\[.*\\]\\)?{slides}") |
778 |
'plain-tex-mode)))) |
'plain-tex-mode)))) |
779 |
(funcall mode))) |
(funcall mode))) |
780 |
|
|
781 |
|
;; `tex-mode' plays two roles: it's the parent of several sub-modes |
782 |
|
;; but it's also the function that chooses between those submodes. |
783 |
|
;; To tell the difference between those two cases where the function |
784 |
|
;; might be called, we check `delay-mode-hooks'. |
785 |
|
(define-derived-mode tex-mode text-mode "generic-TeX" |
786 |
|
(tex-common-initialization)) |
787 |
|
;; We now move the function and define it again. This gives a warning |
788 |
|
;; in the byte-compiler :-( but it's difficult to avoid because |
789 |
|
;; `define-derived-mode' will necessarily define the function once |
790 |
|
;; and we need to define it a second time for `autoload' to get the |
791 |
|
;; proper docstring. |
792 |
|
(defalias 'tex-mode-internal (symbol-function 'tex-mode)) |
793 |
|
;;;###autoload |
794 |
|
(defun tex-mode () |
795 |
|
"Major mode for editing files of input for TeX, LaTeX, or SliTeX. |
796 |
|
Tries to determine (by looking at the beginning of the file) whether |
797 |
|
this file is for plain TeX, LaTeX, or SliTeX and calls `plain-tex-mode', |
798 |
|
`latex-mode', or `slitex-mode', respectively. If it cannot be determined, |
799 |
|
such as if there are no commands in the file, the value of `tex-default-mode' |
800 |
|
says which mode to use." |
801 |
|
(interactive) |
802 |
|
(if delay-mode-hooks |
803 |
|
;; We're called from one of the children already. |
804 |
|
(tex-mode-internal) |
805 |
|
(tex-guess-mode))) |
806 |
|
|
807 |
;;;###autoload |
;;;###autoload |
808 |
(defalias 'TeX-mode 'tex-mode) |
(defalias 'TeX-mode 'tex-mode) |
809 |
;;;###autoload |
;;;###autoload |
812 |
(defalias 'LaTeX-mode 'latex-mode) |
(defalias 'LaTeX-mode 'latex-mode) |
813 |
|
|
814 |
;;;###autoload |
;;;###autoload |
815 |
(define-derived-mode plain-tex-mode text-mode "TeX" |
(define-derived-mode plain-tex-mode tex-mode "TeX" |
816 |
"Major mode for editing files of input for plain TeX. |
"Major mode for editing files of input for plain TeX. |
817 |
Makes $ and } display the characters they match. |
Makes $ and } display the characters they match. |
818 |
Makes \" insert `` when it seems to be the beginning of a quotation, |
Makes \" insert `` when it seems to be the beginning of a quotation, |
852 |
Entering Plain-tex mode runs the hook `text-mode-hook', then the hook |
Entering Plain-tex mode runs the hook `text-mode-hook', then the hook |
853 |
`tex-mode-hook', and finally the hook `plain-tex-mode-hook'. When the |
`tex-mode-hook', and finally the hook `plain-tex-mode-hook'. When the |
854 |
special subshell is initiated, the hook `tex-shell-hook' is run." |
special subshell is initiated, the hook `tex-shell-hook' is run." |
855 |
(tex-common-initialization) |
(set (make-local-variable 'tex-command) tex-run-command) |
856 |
(setq tex-command tex-run-command) |
(set (make-local-variable 'tex-start-of-header) "%\\*\\*start of header") |
857 |
(setq tex-start-of-header "%\\*\\*start of header") |
(set (make-local-variable 'tex-end-of-header) "%\\*\\*end of header") |
858 |
(setq tex-end-of-header "%\\*\\*end of header") |
(set (make-local-variable 'tex-trailer) "\\bye\n")) |
|
(setq tex-trailer "\\bye\n") |
|
|
(run-hooks 'tex-mode-hook)) |
|
859 |
|
|
860 |
;;;###autoload |
;;;###autoload |
861 |
(define-derived-mode latex-mode text-mode "LaTeX" |
(define-derived-mode latex-mode tex-mode "LaTeX" |
862 |
"Major mode for editing files of input for LaTeX. |
"Major mode for editing files of input for LaTeX. |
863 |
Makes $ and } display the characters they match. |
Makes $ and } display the characters they match. |
864 |
Makes \" insert `` when it seems to be the beginning of a quotation, |
Makes \" insert `` when it seems to be the beginning of a quotation, |
898 |
Entering Latex mode runs the hook `text-mode-hook', then |
Entering Latex mode runs the hook `text-mode-hook', then |
899 |
`tex-mode-hook', and finally `latex-mode-hook'. When the special |
`tex-mode-hook', and finally `latex-mode-hook'. When the special |
900 |
subshell is initiated, `tex-shell-hook' is run." |
subshell is initiated, `tex-shell-hook' is run." |
901 |
(tex-common-initialization) |
(set (make-local-variable 'tex-command) latex-run-command) |
902 |
(setq tex-command latex-run-command) |
(set (make-local-variable 'tex-start-of-header) |
903 |
(setq tex-start-of-header "\\\\document\\(style\\|class\\)") |
"\\\\document\\(style\\|class\\)") |
904 |
(setq tex-end-of-header "\\\\begin\\s-*{document}") |
(set (make-local-variable 'tex-end-of-header) "\\\\begin\\s-*{document}") |
905 |
(setq tex-trailer "\\end\\s-*{document}\n") |
(set (make-local-variable 'tex-trailer) "\\end\\s-*{document}\n") |
906 |
;; A line containing just $$ is treated as a paragraph separator. |
;; A line containing just $$ is treated as a paragraph separator. |
907 |
;; A line starting with $$ starts a paragraph, |
;; A line starting with $$ starts a paragraph, |
908 |
;; but does not separate paragraphs if it has more stuff on it. |
;; but does not separate paragraphs if it has more stuff on it. |
909 |
(setq paragraph-start |
(setq paragraph-start |
910 |
(concat "[\f%]\\|[ \t]*\\($\\|\\$\\$\\|" |
(concat "[ \t]*\\(\\$\\$\\|" |
911 |
"\\\\[][]\\|" |
"\\\\[][]\\|" |
912 |
"\\\\" (regexp-opt (append |
"\\\\" (regexp-opt (append |
913 |
(mapcar 'car latex-section-alist) |
(mapcar 'car latex-section-alist) |
937 |
(set (make-local-variable 'outline-regexp) latex-outline-regexp) |
(set (make-local-variable 'outline-regexp) latex-outline-regexp) |
938 |
(set (make-local-variable 'outline-level) 'latex-outline-level) |
(set (make-local-variable 'outline-level) 'latex-outline-level) |
939 |
(set (make-local-variable 'forward-sexp-function) 'latex-forward-sexp) |
(set (make-local-variable 'forward-sexp-function) 'latex-forward-sexp) |
940 |
(set (make-local-variable 'skeleton-end-hook) nil) |
(set (make-local-variable 'skeleton-end-hook) nil)) |
|
(run-hooks 'tex-mode-hook)) |
|
941 |
|
|
942 |
;;;###autoload |
;;;###autoload |
943 |
(define-derived-mode slitex-mode latex-mode "SliTeX" |
(define-derived-mode slitex-mode latex-mode "SliTeX" |
985 |
(setq tex-start-of-header "\\\\documentstyle{slides}\\|\\\\documentclass{slides}")) |
(setq tex-start-of-header "\\\\documentstyle{slides}\\|\\\\documentclass{slides}")) |
986 |
|
|
987 |
(defun tex-common-initialization () |
(defun tex-common-initialization () |
|
(set-syntax-table tex-mode-syntax-table) |
|
988 |
;; Regexp isearch should accept newline and formfeed as whitespace. |
;; Regexp isearch should accept newline and formfeed as whitespace. |
989 |
(set (make-local-variable 'search-whitespace-regexp) "[ \t\r\n\f]+") |
(set (make-local-variable 'search-whitespace-regexp) "[ \t\r\n\f]+") |
990 |
;; A line containing just $$ is treated as a paragraph separator. |
;; A line containing just $$ is treated as a paragraph separator. |
1091 |
(num-matches 0)) |
(num-matches 0)) |
1092 |
(with-output-to-temp-buffer "*Occur*" |
(with-output-to-temp-buffer "*Occur*" |
1093 |
(princ "Mismatches:\n") |
(princ "Mismatches:\n") |
1094 |
(save-excursion |
(with-current-buffer standard-output |
|
(set-buffer standard-output) |
|
1095 |
(occur-mode) |
(occur-mode) |
1096 |
;; This won't actually work...Really, this whole thing should |
;; This won't actually work...Really, this whole thing should |
1097 |
;; be rewritten instead of being a hack on top of occur. |
;; be rewritten instead of being a hack on top of occur. |
1108 |
(forward-char 2)) |
(forward-char 2)) |
1109 |
(goto-char (setq prev-end (point-min)))) |
(goto-char (setq prev-end (point-min)))) |
1110 |
(or (tex-validate-region (point) end) |
(or (tex-validate-region (point) end) |
1111 |
(let* ((oend end) |
(let* ((end (line-beginning-position 2)) |
|
(end (save-excursion (forward-line 1) (point))) |
|
1112 |
start tem) |
start tem) |
1113 |
(beginning-of-line) |
(beginning-of-line) |
1114 |
(setq start (point)) |
(setq start (point)) |
1214 |
|
|
1215 |
(defvar latex-block-default "enumerate") |
(defvar latex-block-default "enumerate") |
1216 |
|
|
1217 |
|
(defvar latex-block-args-alist |
1218 |
|
'(("array" nil ?\{ (skeleton-read "[options]: ") ?\}) |
1219 |
|
("tabular" nil ?\{ (skeleton-read "[options]: ") ?\})) |
1220 |
|
"Skeleton element to use for arguments to particular environments. |
1221 |
|
Every element of the list has the form (NAME . SKEL-ELEM) where NAME is |
1222 |
|
the name of the environment and SKEL-ELEM is an element to use in |
1223 |
|
a skeleton (see `skeleton-insert').") |
1224 |
|
|
1225 |
|
(defvar latex-block-body-alist |
1226 |
|
'(("enumerate" nil '(latex-insert-item) > _) |
1227 |
|
("itemize" nil '(latex-insert-item) > _) |
1228 |
|
("table" nil "\\caption{" > - "}" > \n _) |
1229 |
|
("figure" nil > _ \n "\\caption{" > _ "}" >)) |
1230 |
|
"Skeleton element to use for the body of particular environments. |
1231 |
|
Every element of the list has the form (NAME . SKEL-ELEM) where NAME is |
1232 |
|
the name of the environment and SKEL-ELEM is an element to use in |
1233 |
|
a skeleton (see `skeleton-insert').") |
1234 |
|
|
1235 |
;; Like tex-insert-braces, but for LaTeX. |
;; Like tex-insert-braces, but for LaTeX. |
1236 |
(defalias 'tex-latex-block 'latex-insert-block) |
(defalias 'tex-latex-block 'latex-insert-block) |
1237 |
(define-skeleton latex-insert-block |
(define-skeleton latex-insert-block |
1238 |
"Create a matching pair of lines \\begin[OPT]{NAME} and \\end{NAME} at point. |
"Create a matching pair of lines \\begin{NAME} and \\end{NAME} at point. |
1239 |
Puts point on a blank line between them." |
Puts point on a blank line between them." |
1240 |
(let ((choice (completing-read (format "LaTeX block name [%s]: " |
(let ((choice (completing-read (format "LaTeX block name [%s]: " |
1241 |
latex-block-default) |
latex-block-default) |
1249 |
(push choice latex-block-names)) |
(push choice latex-block-names)) |
1250 |
choice) |
choice) |
1251 |
\n "\\begin{" str "}" |
\n "\\begin{" str "}" |
1252 |
?\[ (skeleton-read "[options]: ") & ?\] | -1 |
(cdr (assoc str latex-block-args-alist)) |
1253 |
> \n _ \n |
> \n (or (cdr (assoc str latex-block-body-alist)) '(nil > _)) \n |
1254 |
"\\end{" str "}" > \n) |
"\\end{" str "}" > \n) |
1255 |
|
|
1256 |
(define-skeleton latex-insert-item |
(define-skeleton latex-insert-item |
1263 |
;;;; LaTeX syntax navigation |
;;;; LaTeX syntax navigation |
1264 |
;;;; |
;;;; |
1265 |
|
|
1266 |
|
(defmacro tex-search-noncomment (&rest body) |
1267 |
|
"Execute BODY as long as it return non-nil and point is in a comment. |
1268 |
|
Return the value returned by the last execution of BODY." |
1269 |
|
(declare (debug t)) |
1270 |
|
(let ((res-sym (make-symbol "result"))) |
1271 |
|
`(let (,res-sym) |
1272 |
|
(while |
1273 |
|
(and (setq ,res-sym (progn ,@body)) |
1274 |
|
(save-excursion (skip-chars-backward "^\n%") (not (bolp))))) |
1275 |
|
,res-sym))) |
1276 |
|
|
1277 |
(defun tex-last-unended-begin () |
(defun tex-last-unended-begin () |
1278 |
"Leave point at the beginning of the last `\\begin{...}' that is unended." |
"Leave point at the beginning of the last `\\begin{...}' that is unended." |
1279 |
(condition-case nil |
(condition-case nil |
1280 |
(while (and (re-search-backward "\\\\\\(begin\\|end\\)\\s *{") |
(while (and (tex-search-noncomment |
1281 |
|
(re-search-backward "\\\\\\(begin\\|end\\)\\s *{")) |
1282 |
(looking-at "\\\\end")) |
(looking-at "\\\\end")) |
1283 |
(tex-last-unended-begin)) |
(tex-last-unended-begin)) |
1284 |
(search-failed (error "Couldn't find unended \\begin")))) |
(search-failed (error "Couldn't find unended \\begin")))) |
1285 |
|
|
1286 |
(defun tex-next-unmatched-end () |
(defun tex-next-unmatched-end () |
1287 |
"Leave point at the end of the next `\\end' that is unended." |
"Leave point at the end of the next `\\end' that is unended." |
1288 |
(while (and (re-search-forward "\\\\\\(begin\\|end\\)\\s *{[^}]+}") |
(while (and (tex-search-noncomment |
1289 |
|
(re-search-forward "\\\\\\(begin\\|end\\)\\s *{[^}]+}")) |
1290 |
(save-excursion (goto-char (match-beginning 0)) |
(save-excursion (goto-char (match-beginning 0)) |
1291 |
(looking-at "\\\\begin"))) |
(looking-at "\\\\begin"))) |
1292 |
(tex-next-unmatched-end))) |
(tex-next-unmatched-end))) |
1558 |
|
|
1559 |
(add-hook 'kill-emacs-hook 'tex-delete-last-temp-files) |
(add-hook 'kill-emacs-hook 'tex-delete-last-temp-files) |
1560 |
|
|
1561 |
|
;; |
1562 |
|
;; Machinery to guess the command that the user wants to execute. |
1563 |
|
;; |
1564 |
|
|
1565 |
|
(defvar tex-compile-history nil) |
1566 |
|
|
1567 |
|
(defvar tex-input-files-re |
1568 |
|
(eval-when-compile |
1569 |
|
(concat "\\." (regexp-opt '("tex" "texi" "texinfo" |
1570 |
|
"bbl" "ind" "sty" "cls") t) |
1571 |
|
;; Include files with no dots (for directories). |
1572 |
|
"\\'\\|\\`[^.]+\\'"))) |
1573 |
|
|
1574 |
|
(defcustom tex-use-reftex t |
1575 |
|
"If non-nil, use RefTeX's list of files to determine what command to use." |
1576 |
|
:type 'boolean) |
1577 |
|
|
1578 |
|
(defvar tex-compile-commands |
1579 |
|
'(((concat "pdf" tex-command |
1580 |
|
" " (if (< 0 (length tex-start-commands)) |
1581 |
|
(shell-quote-argument tex-start-commands)) " %f") |
1582 |
|
t "%r.pdf") |
1583 |
|
((concat tex-command |
1584 |
|
" " (if (< 0 (length tex-start-commands)) |
1585 |
|
(shell-quote-argument tex-start-commands)) " %f") |
1586 |
|
t "%r.dvi") |
1587 |
|
("xdvi %r &" "%r.dvi") |
1588 |
|
("advi %r &" "%r.dvi") |
1589 |
|
("bibtex %r" "%r.aux" "%r.bbl") |
1590 |
|
("makeindex %r" "%r.idx" "%r.ind") |
1591 |
|
("texindex %r.??") |
1592 |
|
("dvipdfm %r" "%r.dvi" "%r.pdf") |
1593 |
|
("dvipdf %r" "%r.dvi" "%r.pdf") |
1594 |
|
("dvips %r" "%r.dvi" "%r.ps") |
1595 |
|
("gv %r.ps &" "%r.ps") |
1596 |
|
("gv %r.pdf &" "%r.pdf") |
1597 |
|
("xpdf %r.pdf &" "%r.pdf") |
1598 |
|
("lpr %r.ps" "%r.ps")) |
1599 |
|
"List of commands for `tex-compile'. |
1600 |
|
Each element should be of the form (FORMAT IN OUT) where |
1601 |
|
FORMAT is an expression that evaluates to a string that can contain |
1602 |
|
- `%r' the main file name without extension. |
1603 |
|
- `%f' the main file name. |
1604 |
|
IN can be either a string (with the same % escapes in it) indicating |
1605 |
|
the name of the input file, or t to indicate that the input is all |
1606 |
|
the TeX files of the document, or nil if we don't know. |
1607 |
|
OUT describes the output file and is either a %-escaped string |
1608 |
|
or nil to indicate that there is no output file.") |
1609 |
|
|
1610 |
|
;; defsubst* gives better byte-code than defsubst. |
1611 |
|
(defsubst* tex-string-prefix-p (str1 str2) |
1612 |
|
"Return non-nil if STR1 is a prefix of STR2" |
1613 |
|
(eq t (compare-strings str2 nil (length str1) str1 nil nil))) |
1614 |
|
|
1615 |
(defun tex-guess-main-file (&optional all) |
(defun tex-guess-main-file (&optional all) |
1616 |
"Find a likely `tex-main-file'. |
"Find a likely `tex-main-file'. |
1617 |
Looks for hints in other buffers in the same directory or in |
Looks for hints in other buffers in the same directory or in |
1618 |
ALL other buffers." |
ALL other buffers. If ALL is `sub' only look at buffers in parent directories |
1619 |
|
of the current buffer." |
1620 |
(let ((dir default-directory) |
(let ((dir default-directory) |
1621 |
(header-re tex-start-of-header)) |
(header-re tex-start-of-header)) |
1622 |
(catch 'found |
(catch 'found |
1623 |
;; Look for a buffer with `tex-main-file' set. |
;; Look for a buffer with `tex-main-file' set. |
1624 |
(dolist (buf (if (consp all) all (buffer-list))) |
(dolist (buf (if (consp all) all (buffer-list))) |
1625 |
(with-current-buffer buf |
(with-current-buffer buf |
1626 |
(when (and (or all (equal dir default-directory)) |
(when (and (cond |
1627 |
|
((null all) (equal dir default-directory)) |
1628 |
|
((eq all 'sub) (tex-string-prefix-p default-directory dir)) |
1629 |
|
(t)) |
1630 |
(stringp tex-main-file)) |
(stringp tex-main-file)) |
1631 |
(throw 'found (expand-file-name tex-main-file))))) |
(throw 'found (expand-file-name tex-main-file))))) |
1632 |
;; Look for a buffer containing the magic `tex-start-of-header'. |
;; Look for a buffer containing the magic `tex-start-of-header'. |
1633 |
(dolist (buf (if (consp all) all (buffer-list))) |
(dolist (buf (if (consp all) all (buffer-list))) |
1634 |
(with-current-buffer buf |
(with-current-buffer buf |
1635 |
(when (and (or all (equal dir default-directory)) |
(when (and (cond |
1636 |
|
((null all) (equal dir default-directory)) |
1637 |
|
((eq all 'sub) (tex-string-prefix-p default-directory dir)) |
1638 |
|
(t)) |
1639 |
buffer-file-name |
buffer-file-name |
1640 |
;; (or (easy-mmode-derived-mode-p 'latex-mode) |
;; (or (easy-mmode-derived-mode-p 'latex-mode) |
1641 |
;; (easy-mmode-derived-mode-p 'plain-tex-mode)) |
;; (easy-mmode-derived-mode-p 'plain-tex-mode)) |
1651 |
"Return the relative name of the main file." |
"Return the relative name of the main file." |
1652 |
(let* ((file (or tex-main-file |
(let* ((file (or tex-main-file |
1653 |
;; Compatibility with AUCTeX. |
;; Compatibility with AUCTeX. |
1654 |
(and (boundp 'TeX-master) (stringp TeX-master) |
(with-no-warnings |
1655 |
(set (make-local-variable 'tex-main-file) TeX-master)) |
(when (and (boundp 'TeX-master) (stringp TeX-master)) |
1656 |
|
(make-local-variable 'tex-main-file) |
1657 |
|
(setq tex-main-file TeX-master))) |
1658 |
;; Try to guess the main file. |
;; Try to guess the main file. |
1659 |
(if (not buffer-file-name) |
(if (not buffer-file-name) |
1660 |
(error "Buffer is not associated with any file") |
(error "Buffer is not associated with any file") |
1667 |
buffer-file-name |
buffer-file-name |
1668 |
;; This isn't the main file, let's try to find better, |
;; This isn't the main file, let's try to find better, |
1669 |
(or (tex-guess-main-file) |
(or (tex-guess-main-file) |
1670 |
|
(tex-guess-main-file 'sub) |
1671 |
;; (tex-guess-main-file t) |
;; (tex-guess-main-file t) |
1672 |
buffer-file-name))))))) |
buffer-file-name))))))) |
1673 |
(if (file-exists-p file) file (concat file ".tex")))) |
(if (file-exists-p file) file (concat file ".tex")))) |
1674 |
|
|
1675 |
|
(defun tex-summarize-command (cmd) |
1676 |
|
(if (not (stringp cmd)) "" |
1677 |
|
(mapconcat 'identity |
1678 |
|
(mapcar (lambda (s) (car (split-string s))) |
1679 |
|
(split-string cmd "\\s-*\\(?:;\\|&&\\)\\s-*")) |
1680 |
|
"&"))) |
1681 |
|
|
1682 |
|
(defun tex-uptodate-p (file) |
1683 |
|
"Return non-nil if FILE is not uptodate w.r.t the document source files. |
1684 |
|
FILE is typically the output DVI or PDF file." |
1685 |
|
;; We should check all the files included !!! |
1686 |
|
(and |
1687 |
|
;; Clearly, the target must exist. |
1688 |
|
(file-exists-p file) |
1689 |
|
;; And the last run must not have asked for a rerun. |
1690 |
|
;; FIXME: this should check that the last run was done on the same file. |
1691 |
|
(let ((buf (condition-case nil (tex-shell-buf) (error nil)))) |
1692 |
|
(when buf |
1693 |
|
(with-current-buffer buf |
1694 |
|
(save-excursion |
1695 |
|
(goto-char (point-max)) |
1696 |
|
(and (re-search-backward |
1697 |
|
"(see the transcript file for additional information)" nil t) |
1698 |
|
(> (save-excursion |
1699 |
|
(or (re-search-backward "\\[[0-9]+\\]" nil t) |
1700 |
|
(point-min))) |
1701 |
|
(save-excursion |
1702 |
|
(or (re-search-backward "Rerun" nil t) |
1703 |
|
(point-min))))))))) |
1704 |
|
;; And the input files must not have been changed in the meantime. |
1705 |
|
(let ((files (if (and tex-use-reftex |
1706 |
|
(fboundp 'reftex-scanning-info-available-p) |
1707 |
|
(reftex-scanning-info-available-p)) |
1708 |
|
(reftex-all-document-files) |
1709 |
|
(list (file-name-directory (expand-file-name file))))) |
1710 |
|
(ignored-dirs-re |
1711 |
|
(concat |
1712 |
|
(regexp-opt |
1713 |
|
(delq nil (mapcar (lambda (s) (if (eq (aref s (1- (length s))) ?/) |
1714 |
|
(substring s 0 (1- (length s))))) |
1715 |
|
completion-ignored-extensions)) |
1716 |
|
t) "\\'")) |
1717 |
|
(uptodate t)) |
1718 |
|
(while (and files uptodate) |
1719 |
|
(let ((f (pop files))) |
1720 |
|
(if (file-directory-p f) |
1721 |
|
(unless (string-match ignored-dirs-re f) |
1722 |
|
(setq files (nconc |
1723 |
|
(directory-files f t tex-input-files-re) |
1724 |
|
files))) |
1725 |
|
(when (file-newer-than-file-p f file) |
1726 |
|
(setq uptodate nil))))) |
1727 |
|
uptodate))) |
1728 |
|
|
1729 |
|
|
1730 |
|
(autoload 'format-spec "format-spec") |
1731 |
|
|
1732 |
|
(defvar tex-executable-cache nil) |
1733 |
|
(defun tex-executable-exists-p (name) |
1734 |
|
"Like `executable-find' but with a cache." |
1735 |
|
(let ((cache (assoc name tex-executable-cache))) |
1736 |
|
(if cache (cdr cache) |
1737 |
|
(let ((executable (executable-find name))) |
1738 |
|
(push (cons name executable) tex-executable-cache) |
1739 |
|
executable)))) |
1740 |
|
|
1741 |
|
(defun tex-command-executable (cmd) |
1742 |
|
(let ((s (if (stringp cmd) cmd (eval (car cmd))))) |
1743 |
|
(substring s 0 (string-match "[ \t]\\|\\'" s)))) |
1744 |
|
|
1745 |
|
(defun tex-command-active-p (cmd fspec) |
1746 |
|
"Return non-nil if the CMD spec might need to be run." |
1747 |
|
(let ((in (nth 1 cmd)) |
1748 |
|
(out (nth 2 cmd))) |
1749 |
|
(if (stringp in) |
1750 |
|
(let ((file (format-spec in fspec))) |
1751 |
|
(when (file-exists-p file) |
1752 |
|
(or (not out) |
1753 |
|
(file-newer-than-file-p |
1754 |
|
file (format-spec out fspec))))) |
1755 |
|
(when (and (eq in t) (stringp out)) |
1756 |
|
(not (tex-uptodate-p (format-spec out fspec))))))) |
1757 |
|
|
1758 |
|
(defun tex-compile-default (fspec) |
1759 |
|
"Guess a default command given the format-spec FSPEC." |
1760 |
|
;; TODO: Learn to do latex+dvips! |
1761 |
|
(let ((cmds nil) |
1762 |
|
(unchanged-in nil)) |
1763 |
|
;; Only consider active commands. |
1764 |
|
(dolist (cmd tex-compile-commands) |
1765 |
|
(when (tex-executable-exists-p (tex-command-executable cmd)) |
1766 |
|
(if (tex-command-active-p cmd fspec) |
1767 |
|
(push cmd cmds) |
1768 |
|
(push (nth 1 cmd) unchanged-in)))) |
1769 |
|
;; Remove those commands whose input was considered stable for |
1770 |
|
;; some other command (typically if (t . "%.pdf") is inactive |
1771 |
|
;; then we're using pdflatex and the fact that the dvi file |
1772 |
|
;; is inexistent doesn't matter). |
1773 |
|
(let ((tmp nil)) |
1774 |
|
(dolist (cmd cmds) |
1775 |
|
(unless (member (nth 1 cmd) unchanged-in) |
1776 |
|
(push cmd tmp))) |
1777 |
|
;; Only remove if there's something left. |
1778 |
|
(if tmp (setq cmds tmp))) |
1779 |
|
;; Remove commands whose input is not uptodate either. |
1780 |
|
(let ((outs (delq nil (mapcar (lambda (x) (nth 2 x)) cmds))) |
1781 |
|
(tmp nil)) |
1782 |
|
(dolist (cmd cmds) |
1783 |
|
(unless (member (nth 1 cmd) outs) |
1784 |
|
(push cmd tmp))) |
1785 |
|
;; Only remove if there's something left. |
1786 |
|
(if tmp (setq cmds tmp))) |
1787 |
|
;; Select which file we're going to operate on (the latest). |
1788 |
|
(let ((latest (nth 1 (car cmds)))) |
1789 |
|
(dolist (cmd (prog1 (cdr cmds) (setq cmds (list (car cmds))))) |
1790 |
|
(if (equal latest (nth 1 cmd)) |
1791 |
|
(push cmd cmds) |
1792 |
|
(unless (eq latest t) ;Can't beat that! |
1793 |
|
(if (or (not (stringp latest)) |
1794 |
|
(eq (nth 1 cmd) t) |
1795 |
|
(and (stringp (nth 1 cmd)) |
1796 |
|
(file-newer-than-file-p |
1797 |
|
(format-spec (nth 1 cmd) fspec) |
1798 |
|
(format-spec latest fspec)))) |
1799 |
|
(setq latest (nth 1 cmd) cmds (list cmd))))))) |
1800 |
|
;; Expand the command spec into the actual text. |
1801 |
|
(dolist (cmd (prog1 cmds (setq cmds nil))) |
1802 |
|
(push (cons (eval (car cmd)) (cdr cmd)) cmds)) |
1803 |
|
;; Select the favorite command from the history. |
1804 |
|
(let ((hist tex-compile-history) |
1805 |
|
re hist-cmd) |
1806 |
|
(while hist |
1807 |
|
(setq hist-cmd (pop hist)) |
1808 |
|
(setq re (concat "\\`" |
1809 |
|
(regexp-quote (tex-command-executable hist-cmd)) |
1810 |
|
"\\([ \t]\\|\\'\\)")) |
1811 |
|
(dolist (cmd cmds) |
1812 |
|
;; If the hist entry uses the same command and applies to a file |
1813 |
|
;; of the same type (e.g. `gv %r.pdf' vs `gv %r.ps'), select cmd. |
1814 |
|
(and (string-match re (car cmd)) |
1815 |
|
(or (not (string-match "%[fr]\\([-._[:alnum:]]+\\)" (car cmd))) |
1816 |
|
(string-match (regexp-quote (match-string 1 (car cmd))) |
1817 |
|
hist-cmd)) |
1818 |
|
(setq hist nil cmds (list cmd))))) |
1819 |
|
;; Substitute and return. |
1820 |
|
(if (and hist-cmd |
1821 |
|
(string-match (concat "[' \t\"]" (format-spec "%r" fspec) |
1822 |
|
"\\([;&' \t\"]\\|\\'\\)") hist-cmd)) |
1823 |
|
;; The history command was already applied to the same file, |
1824 |
|
;; so just reuse it. |
1825 |
|
hist-cmd |
1826 |
|
(if cmds (format-spec (caar cmds) fspec)))))) |
1827 |
|
|
1828 |
|
(defun tex-compile (dir cmd) |
1829 |
|
"Run a command CMD on current TeX buffer's file in DIR." |
1830 |
|
;; FIXME: Use time-stamps on files to decide the next op. |
1831 |
|
(interactive |
1832 |
|
(let* ((file (tex-main-file)) |
1833 |
|
(default-directory |
1834 |
|
(prog1 (file-name-directory (expand-file-name file)) |
1835 |
|
(setq file (file-name-nondirectory file)))) |
1836 |
|
(root (file-name-sans-extension file)) |
1837 |
|
(fspec (list (cons ?r (comint-quote-filename root)) |
1838 |
|
(cons ?f (comint-quote-filename file)))) |
1839 |
|
(default (tex-compile-default fspec))) |
1840 |
|
(list default-directory |
1841 |
|
(completing-read |
1842 |
|
(format "Command [%s]: " (tex-summarize-command default)) |
1843 |
|
(mapcar (lambda (x) |
1844 |
|
(list (format-spec (eval (car x)) fspec))) |
1845 |
|
tex-compile-commands) |
1846 |
|
nil nil nil 'tex-compile-history default)))) |
1847 |
|
(save-some-buffers (not compilation-ask-about-save) nil) |
1848 |
|
(if (tex-shell-running) |
1849 |
|
(tex-kill-job) |
1850 |
|
(tex-start-shell)) |
1851 |
|
(tex-send-tex-command cmd dir)) |
1852 |
|
|
1853 |
(defun tex-start-tex (command file &optional dir) |
(defun tex-start-tex (command file &optional dir) |
1854 |
"Start a TeX run, using COMMAND on FILE." |
"Start a TeX run, using COMMAND on FILE." |
2136 |
line LINE of the window, or centered if LINE is nil." |
line LINE of the window, or centered if LINE is nil." |
2137 |
(interactive "P") |
(interactive "P") |
2138 |
(let ((tex-shell (get-buffer "*tex-shell*")) |
(let ((tex-shell (get-buffer "*tex-shell*")) |
|
(old-buffer (current-buffer)) |
|
2139 |
(window)) |
(window)) |
2140 |
(if (null tex-shell) |
(if (null tex-shell) |
2141 |
(message "No TeX output buffer") |
(message "No TeX output buffer") |
2252 |
(defvar tex-indent-item tex-indent-basic) |
(defvar tex-indent-item tex-indent-basic) |
2253 |
(defvar tex-indent-item-re "\\\\\\(bib\\)?item\\>") |
(defvar tex-indent-item-re "\\\\\\(bib\\)?item\\>") |
2254 |
|
|
2255 |
(easy-mmode-defsyntax tex-latex-indent-syntax-table |
(defvar tex-latex-indent-syntax-table |
2256 |
'((?$ . ".") |
(let ((st (make-syntax-table tex-mode-syntax-table))) |
2257 |
(?\( . ".") |
(modify-syntax-entry ?$ "." st) |
2258 |
(?\) . ".")) |
(modify-syntax-entry ?\( "." st) |
2259 |
"Syntax table used while computing indentation." |
(modify-syntax-entry ?\) "." st) |
2260 |
:copy tex-mode-syntax-table) |
st) |
2261 |
|
"Syntax table used while computing indentation.") |
2262 |
|
|
2263 |
(defun latex-indent (&optional arg) |
(defun latex-indent (&optional arg) |
2264 |
(if (and (eq (get-text-property (line-beginning-position) 'face) |
(if (and (eq (get-text-property (line-beginning-position) 'face) |
2265 |
tex-verbatim-face)) |
tex-verbatim-face)) |
2266 |
(indent-relative) |
'noindent |
2267 |
(with-syntax-table tex-latex-indent-syntax-table |
(with-syntax-table tex-latex-indent-syntax-table |
2268 |
;; TODO: Rather than ignore $, we should try to be more clever about it. |
;; TODO: Rather than ignore $, we should try to be more clever about it. |
2269 |
(let ((indent |
(let ((indent |
2350 |
(min (current-column) (+ tex-indent-arg col)) |
(min (current-column) (+ tex-indent-arg col)) |
2351 |
(skip-syntax-forward " ") |
(skip-syntax-forward " ") |
2352 |
(current-column)))))))))) |
(current-column)))))))))) |
2353 |
|
;;; DocTeX support |
2354 |
|
|
2355 |
|
(defun doctex-font-lock-^^A () |
2356 |
|
(if (eq (char-after (line-beginning-position)) ?\%) |
2357 |
|
(progn |
2358 |
|
(put-text-property |
2359 |
|
(1- (match-beginning 1)) (match-beginning 1) |
2360 |
|
'syntax-table |
2361 |
|
(if (= (1+ (line-beginning-position)) (match-beginning 1)) |
2362 |
|
;; The `%' is a single-char comment, which Emacs |
2363 |
|
;; syntax-table can't deal with. We could turn it |
2364 |
|
;; into a non-comment, or use `\n%' or `%^' as the comment. |
2365 |
|
;; Instead, we include it in the ^^A comment. |
2366 |
|
(eval-when-compile (string-to-syntax "< b")) |
2367 |
|
(eval-when-compile (string-to-syntax ">")))) |
2368 |
|
(let ((end (line-end-position))) |
2369 |
|
(if (< end (point-max)) |
2370 |
|
(put-text-property |
2371 |
|
end (1+ end) |
2372 |
|
'syntax-table |
2373 |
|
(eval-when-compile (string-to-syntax "> b"))))) |
2374 |
|
(eval-when-compile (string-to-syntax "< b"))))) |
2375 |
|
|
2376 |
|
(defun doctex-font-lock-syntactic-face-function (state) |
2377 |
|
;; Mark DocTeX documentation, which is parsed as a style A comment |
2378 |
|
;; starting in column 0. |
2379 |
|
(if (or (nth 3 state) (nth 7 state) |
2380 |
|
(not (memq (char-before (nth 8 state)) |
2381 |
|
'(?\n nil)))) |
2382 |
|
;; Anything else is just as for LaTeX. |
2383 |
|
(tex-font-lock-syntactic-face-function state) |
2384 |
|
font-lock-doc-face)) |
2385 |
|
|
2386 |
|
(defvar doctex-font-lock-syntactic-keywords |
2387 |
|
(append |
2388 |
|
tex-font-lock-syntactic-keywords |
2389 |
|
;; For DocTeX comment-in-doc. |
2390 |
|
`(("\\(\\^\\)\\^A" (1 (doctex-font-lock-^^A)))))) |
2391 |
|
|
2392 |
|
(defvar doctex-font-lock-keywords |
2393 |
|
(append tex-font-lock-keywords |
2394 |
|
'(("^%<[^>]*>" (0 font-lock-preprocessor-face t))))) |
2395 |
|
|
2396 |
|
;;;###autoload |
2397 |
|
(define-derived-mode doctex-mode latex-mode "DocTeX" |
2398 |
|
"Major mode to edit DocTeX files." |
2399 |
|
(setq font-lock-defaults |
2400 |
|
(cons (append (car font-lock-defaults) '(doctex-font-lock-keywords)) |
2401 |
|
(mapcar |
2402 |
|
(lambda (x) |
2403 |
|
(case (car-safe x) |
2404 |
|
(font-lock-syntactic-keywords |
2405 |
|
(cons (car x) 'doctex-font-lock-syntactic-keywords)) |
2406 |
|
(font-lock-syntactic-face-function |
2407 |
|
(cons (car x) 'doctex-font-lock-syntactic-face-function)) |
2408 |
|
(t x))) |
2409 |
|
(cdr font-lock-defaults))))) |
2410 |
|
|
2411 |
(run-hooks 'tex-mode-load-hook) |
(run-hooks 'tex-mode-load-hook) |
2412 |
|
|
2413 |
(provide 'tex-mode) |
(provide 'tex-mode) |
2414 |
|
|
2415 |
|
;;; arch-tag: c0a680b1-63aa-4547-84b9-4193c29c0080 |
2416 |
;;; tex-mode.el ends here |
;;; tex-mode.el ends here |