# HG changeset patch # User Stefan Monnier # Date 1036774617 0 # Node ID 14763abde487d0767522b385e8f79db22af66873 # Parent 55c749e7394a8199d3f02b667290160e27c6e9b8 (latex-standard-block-names): Rename from standard-latex-block-names. (tex-font-lock-keywords-1): Add providecommand, renewenvironment, and renewtheorem. Highlight \it and \bf separately since they may overlap. (tex-font-lock-suscript, tex-font-lock-unfontify-region): New funs. (tex-font-lock-keywords-3, tex-verbatim-environments) (tex-font-lock-syntactic-keywords): New vars. (superscript, subscript, tex-verbatim-face): New faces. (tex-font-lock-syntactic-face-function): Handle \verb construct. (tex-common-initialization): Update font-lock-defaults setting. (tex-insert-braces): Make it into a skeleton. (latex-fill-nobreak-predicate): Don't break after \. (latex-insert-block): Rename from tex-latex-block. (latex-down-list): Use tex-mode-syntax-table when skipping parens rather than the indentation syntax-table. (latex-close-block): Rename from tex-close-latex-block. (latex-split-block): New fun. (latex-indent): Don't indent inside a verbatim block. (latex-find-indent): Stick \begin{verbatim} to the margin. diff -r 55c749e7394a -r 14763abde487 lisp/textmodes/tex-mode.el --- a/lisp/textmodes/tex-mode.el Fri Nov 08 12:32:10 2002 +0000 +++ b/lisp/textmodes/tex-mode.el Fri Nov 08 16:56:57 2002 +0000 @@ -141,7 +141,7 @@ :group 'tex-run :version "21.4") -(defvar standard-latex-block-names +(defvar latex-standard-block-names '("abstract" "array" "center" "description" "displaymath" "document" "enumerate" "eqnarray" "eqnarray*" "equation" "figure" "figure*" @@ -156,7 +156,7 @@ ;;;###autoload (defcustom latex-block-names nil "*User defined LaTeX block names. -Combined with `standard-latex-block-names' for minibuffer completion." +Combined with `latex-standard-block-names' for minibuffer completion." :type '(repeat string) :group 'tex-run) @@ -437,8 +437,9 @@ '("title" "begin" "end" "chapter" "part" "section" "subsection" "subsubsection" "paragraph" "subparagraph" "subsubparagraph" - "newcommand" "renewcommand" "newenvironment" - "newtheorem") + "newcommand" "renewcommand" "providecommand" + "newenvironment" "renewenvironment" + "newtheorem" "renewtheorem") t)) (variables (regexp-opt '("newcounter" "newcounter*" "setcounter" "addtocounter" @@ -479,8 +480,8 @@ ;; as improves the behavior in the very rare case where you do ;; have a comment in ARG. 3 'font-lock-function-name-face 'keep) - (list (concat slash "\\(re\\)?newcommand\\** *\\(\\\\[A-Za-z@]+\\)") - 2 'font-lock-function-name-face 'keep) + (list (concat slash "\\(?:provide\\|\\(?:re\\)?new\\)command\\** *\\(\\\\[A-Za-z@]+\\)") + 1 'font-lock-function-name-face 'keep) ;; Variable args. (list (concat slash variables " *" arg) 2 'font-lock-variable-name-face) ;; Include args. @@ -521,6 +522,7 @@ ;; Miscellany. (slash "\\\\") (opt " *\\(\\[[^]]*\\] *\\)*") + (args "\\(\\(?:[^{}&\\]+\\|\\\\.\\|{[^}]*}\\)+\\)") (arg "{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)")) (list ;; @@ -546,23 +548,105 @@ ;; (list (concat slash type arg) 2 '(quote bold-italic) 'append) ;; ;; Old-style bf/em/it/sl. Stop at `\\' and un-escaped `&', for tables. - (list (concat "\\\\\\(\\(bf\\)\\|em\\|it\\|sl\\)\\>" - "\\(\\([^}&\\]\\|\\\\[^\\]\\)+\\)") - 3 '(if (match-beginning 2) 'bold 'italic) 'append))))) + (list (concat "\\\\\\(em\\|it\\|sl\\)\\>" args) + 2 '(quote italic) 'append) + ;; This is separate from the previous one because of cases like + ;; {\em foo {\bf bar} bla} where both match. + (list (concat "\\\\bf\\>" args) 1 '(quote bold) 'append))))) "Gaudy expressions to highlight in TeX modes.") +(defun tex-font-lock-suscript (pos) + (unless (or (memq (get-text-property pos 'face) + '(font-lock-constant-face font-lock-builtin-face + font-lock-comment-face tex-verbatim-face)) + ;; Check for backslash quoting + (let ((odd nil) + (pos pos)) + (while (eq (char-before pos) ?\\) + (setq pos (1- pos) odd (not odd))) + odd)) + (if (eq (char-after pos) ?_) + '(face subscript display (raise -0.3)) + '(face superscript display (raise +0.3))))) + +(defconst tex-font-lock-keywords-3 + (append tex-font-lock-keywords-2 + (eval-when-compile + (let ((general "\\([a-zA-Z@]+\\|[^ \t\n]\\)") + (slash "\\\\") + ;; This is not the same regexp as before: it has a `+' removed. + ;; The + makes the matching faster in the above cases (where we can + ;; exit as soon as the match fails) but would make this matching + ;; degenerate to nasty complexity (because we try to match the + ;; closing brace, which forces trying all matching combinations). + (arg "{\\(?:[^{}\\]\\|\\\\.\\|{[^}]*}\\)*")) + `((,(concat "[_^] *\\([^\n\\{}]\\|" slash general "\\|" arg "}\\)") + (1 (tex-font-lock-suscript (match-beginning 0)) + append)))))) + "Experimental expressions to highlight in TeX modes.") + (defvar tex-font-lock-keywords tex-font-lock-keywords-1 "Default expressions to highlight in TeX modes.") +(defvar tex-verbatim-environments + '("verbatim" "verbatim*")) + +(defvar tex-font-lock-syntactic-keywords + (let ((verbs (regexp-opt tex-verbatim-environments t))) + `((,(concat "^\\\\begin *{" verbs "}.*\\(\n\\)") 2 "|") + (,(concat "^\\\\end *{" verbs "}\\(.?\\)") 2 + (unless (<= (match-beginning 0) (point-min)) + (put-text-property (1- (match-beginning 0)) (match-beginning 0) + 'syntax-table (string-to-syntax "|")) + "<")) + ;; ("^\\(\\\\\\)begin *{comment}" 1 "< b") + ;; ("^\\\\end *{comment}.*\\(\n\\)" 1 "> b") + ("\\\\verb\\**\\([^a-z@*]\\)" 1 "\"")))) + +(defun tex-font-lock-unfontify-region (beg end) + (font-lock-default-unfontify-region beg end) + (while (< beg end) + (let ((next (next-single-property-change beg 'display nil end)) + (prop (get-text-property beg 'display))) + (if (and (eq (car-safe prop) 'raise) + (member (car-safe (cdr prop)) '(-0.3 +0.3)) + (null (cddr prop))) + (put-text-property beg next 'display nil)) + (setq beg next)))) + +(defface superscript + '((t :height 0.8)) ;; :raise 0.3 + "Face used for superscripts.") +(defface subscript + '((t :height 0.8)) ;; :raise -0.3 + "Face used for subscripts.") (defface tex-math-face '((t :inherit font-lock-string-face)) "Face used to highlight TeX math expressions.") (defvar tex-math-face 'tex-math-face) +(defface tex-verbatim-face + ;; '((t :inherit font-lock-string-face)) + '((t :family "courier")) + "Face used to highlight TeX verbatim environments.") +(defvar tex-verbatim-face 'tex-verbatim-face) ;; Use string syntax but math face for $...$. (defun tex-font-lock-syntactic-face-function (state) - (if (nth 3 state) tex-math-face font-lock-comment-face)) + (let ((char (nth 3 state))) + (cond + ((not char) font-lock-comment-face) + ((eq char ?$) tex-math-face) + (t + (when (char-valid-p char) + ;; This is a \verb?...? construct. Let's find the end and mark it. + (save-excursion + (skip-chars-forward (string ?^ char)) ;; Use `end' ? + (when (eq (char-syntax (preceding-char)) ?/) + (put-text-property (1- (point)) (point) 'syntax-table '(1))) + (unless (eobp) + (put-text-property (point) (1+ (point)) 'syntax-table '(7))))) + tex-verbatim-face)))) (defun tex-define-common-keys (keymap) @@ -607,8 +691,8 @@ (define-key map "\C-c\C-b" 'tex-buffer) (define-key map "\C-c\C-f" 'tex-file) (define-key map "\C-c\C-i" 'tex-bibtex-file) - (define-key map "\C-c\C-o" 'tex-latex-block) - (define-key map "\C-c\C-e" 'tex-close-latex-block) + (define-key map "\C-c\C-o" 'latex-insert-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) (define-key map [(control return)] 'tex-feed-input) @@ -904,15 +988,21 @@ (set (make-local-variable 'facemenu-end-add-face) "}") (set (make-local-variable 'facemenu-remove-face-function) t) (set (make-local-variable 'font-lock-defaults) - '((tex-font-lock-keywords - tex-font-lock-keywords-1 tex-font-lock-keywords-2) + '((tex-font-lock-keywords tex-font-lock-keywords-1 + tex-font-lock-keywords-2 tex-font-lock-keywords-3) nil nil ((?$ . "\"")) nil ;; Who ever uses that anyway ??? (font-lock-mark-block-function . mark-paragraph) (font-lock-syntactic-face-function - . tex-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))) ;; 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. (make-local-variable 'tex-command) (make-local-variable 'tex-start-of-header) (make-local-variable 'tex-end-of-header) @@ -1081,39 +1171,41 @@ (message "Paragraph being closed appears to contain a mismatch")) (insert "\n\n")) -(defun tex-insert-braces () +(define-skeleton tex-insert-braces "Make a pair of braces and be poised to type inside of them." - (interactive "*") - (insert ?\{) - (save-excursion - (insert ?}))) + nil + ?\{ _ ?}) ;; This function is used as the value of fill-nobreak-predicate ;; in LaTeX mode. Its job is to prevent line-breaking inside ;; of a \verb construct. (defun latex-fill-nobreak-predicate () - (let ((opoint (point)) - inside) - (save-excursion - (beginning-of-line) - (while (re-search-forward "\\\\verb\\(.\\)" opoint t) - (unless (re-search-forward (regexp-quote (match-string 1)) opoint t) - (setq inside t)))) - inside)) + (save-excursion + (skip-chars-backward " ") + ;; Don't break after \ since `\ ' has special meaning. + (or (and (not (bobp)) (memq (char-syntax (char-before)) '(?\\ ?/))) + (let ((opoint (point)) + inside) + (beginning-of-line) + (while (re-search-forward "\\\\verb\\(.\\)" opoint t) + (unless (re-search-forward (regexp-quote (match-string 1)) opoint t) + (setq inside t))) + inside)))) (defvar latex-block-default "enumerate") ;; Like tex-insert-braces, but for LaTeX. -(define-skeleton tex-latex-block +(defalias 'tex-latex-block 'latex-insert-block) +(define-skeleton latex-insert-block "Create a matching pair of lines \\begin[OPT]{NAME} and \\end{NAME} at point. Puts point on a blank line between them." (let ((choice (completing-read (format "LaTeX block name [%s]: " latex-block-default) (append latex-block-names - standard-latex-block-names) + latex-standard-block-names) nil nil nil nil latex-block-default))) (setq latex-block-default choice) - (unless (or (member choice standard-latex-block-names) + (unless (or (member choice latex-standard-block-names) (member choice latex-block-names)) ;; Remember new block names for later completion. (push choice latex-block-names)) @@ -1229,23 +1321,32 @@ (down-list 1) (forward-sexp 1) ;; Skip arguments. - (while (looking-at "[ \t]*\\s(") (forward-sexp))))) + (while (looking-at "[ \t]*[[{(]") + (with-syntax-table tex-mode-syntax-table + (forward-sexp)))))) + +(defalias 'tex-close-latex-block 'latex-close-block) +(define-skeleton latex-close-block + "Create an \\end{...} to match the last unclosed \\begin{...}." + (save-excursion + (tex-last-unended-begin) + (if (not (looking-at "\\\\begin\\(\\s *{[^}\n]*}\\)")) '("{" _ "}") + (match-string 1))) + \n "\\end" str > \n) -(defun tex-close-latex-block () - "Creates an \\end{...} to match the last unclosed \\begin{...}." - (interactive "*") - (let ((new-line-needed (bolp)) - text indentation) - (save-excursion - (condition-case nil - (tex-last-unended-begin) - (error (error "Couldn't find unended \\begin"))) - (setq indentation (current-column)) - (re-search-forward "\\\\begin\\(\\s *{[^}\n]*}\\)") - (setq text (buffer-substring (match-beginning 1) (match-end 1)))) - (indent-to indentation) - (insert "\\end" text) - (if new-line-needed (insert ?\n)))) +(define-skeleton latex-split-block + "Split the enclosing environment by inserting \\end{..}\\begin{..} at point." + (save-excursion + (tex-last-unended-begin) + (if (not (looking-at "\\\\begin\\(\\s *{[^}\n]*}\\)")) '("{" _ "}") + (prog1 (match-string 1) + (goto-char (match-end 1)) + (setq v1 (buffer-substring (point) + (progn + (while (looking-at "[ \t]*[[{]") + (forward-sexp 1)) + (point))))))) + \n "\\end" str > \n _ \n "\\begin" str v1 > \n) (defconst tex-discount-args-cmds '("begin" "end" "input" "special" "cite" "ref" "include" "includeonly" @@ -1866,16 +1967,19 @@ :copy tex-mode-syntax-table) (defun latex-indent (&optional arg) - (with-syntax-table tex-latex-indent-syntax-table - ;; TODO: Rather than ignore $, we should try to be more clever about it. - (let ((indent - (save-excursion - (beginning-of-line) - (latex-find-indent)))) - (if (< indent 0) (setq indent 0)) - (if (<= (current-column) (current-indentation)) - (indent-line-to indent) - (save-excursion (indent-line-to indent)))))) + (if (and (eq (get-text-property (line-beginning-position) 'face) + tex-verbatim-face)) + (indent-relative) + (with-syntax-table tex-latex-indent-syntax-table + ;; TODO: Rather than ignore $, we should try to be more clever about it. + (let ((indent + (save-excursion + (beginning-of-line) + (latex-find-indent)))) + (if (< indent 0) (setq indent 0)) + (if (<= (current-column) (current-indentation)) + (indent-line-to indent) + (save-excursion (indent-line-to indent))))))) (defun latex-find-indent (&optional virtual) "Find the proper indentation of text after point. @@ -1885,9 +1989,15 @@ (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 (>= (current-indentation) (current-column)) - (current-indentation)) + (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 @@ -1918,7 +2028,7 @@ (> pos (progn (latex-down-list) (forward-comment (point-max)) (point)))) - ;; Align with the first element after the open-paren. + ;; 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) @@ -1928,8 +2038,8 @@ ;; 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. + ((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))) @@ -1937,11 +2047,11 @@ ;; 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))) + (+ 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 " ")