]> code.delx.au - gnu-emacs/commitdiff
(latex-standard-block-names): Rename from standard-latex-block-names.
authorStefan Monnier <monnier@iro.umontreal.ca>
Fri, 8 Nov 2002 16:56:57 +0000 (16:56 +0000)
committerStefan Monnier <monnier@iro.umontreal.ca>
Fri, 8 Nov 2002 16:56:57 +0000 (16:56 +0000)
(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.

lisp/textmodes/tex-mode.el

index f3f7e9dc685c3bfe31177a55d1e83b37808e7272..e5635777985a43d50d1ad67eb6b2e06af250197d 100644 (file)
@@ -141,7 +141,7 @@ If nil, no commands are used.  See the documentation of `tex-command'."
   :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 @@ If nil, no commands are used.  See the documentation of `tex-command'."
 ;;;###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 @@ An alternative value is \" . \", if you use a font with a narrow period."
                      '("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 @@ An alternative value is \" . \", if you use a font with a narrow period."
             ;; 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 @@ An alternative value is \" . \", if you use a font with a narrow period."
            ;; Miscellany.
            (slash "\\\\")
            (opt " *\\(\\[[^]]*\\] *\\)*")
+           (args "\\(\\(?:[^{}&\\]+\\|\\\\.\\|{[^}]*}\\)+\\)")
            (arg "{\\(\\(?:[^{}\\]+\\|\\\\.\\|{[^}]*}\\)+\\)"))
        (list
        ;;
@@ -546,23 +548,105 @@ An alternative value is \" . \", if you use a font with a narrow period."
        ;; (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)
@@ -607,8 +691,8 @@ An alternative value is \" . \", if you use a font with a narrow period."
     (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 @@ Entering SliTeX mode runs the hook `text-mode-hook', then the hook
   (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 @@ A prefix arg inhibits the checking."
       (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 @@ Mark is left at original location."
        (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"
@@ -1866,16 +1967,19 @@ Runs the shell command defined by `tex-show-queue-command'."
   :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 @@ There might be text before 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
@@ -1918,7 +2028,7 @@ There might be text before point."
                  (> 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 @@ There might be text before point."
         ;; 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 @@ There might be text before point."
               ;; 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 " ")