: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*"
;;;###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)
'("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"
;; 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.
;; Miscellany.
(slash "\\\\")
(opt " *\\(\\[[^]]*\\] *\\)*")
+ (args "\\(\\(?:[^{}&\\]+\\|\\\\.\\|{[^}]*}\\)+\\)")
(arg "{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)"))
(list
;;
;; (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))))
\f
(defun tex-define-common-keys (keymap)
(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)
(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)
(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))
(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))))))
-(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))))
+(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)
+
+(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"
: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.
(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
(> 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)
;; 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)))
;; 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 " ")