From becff06c71d277647eda4378203d03ab36e141eb Mon Sep 17 00:00:00 2001 From: mattkae Date: Tue, 17 May 2022 07:07:37 -0400 Subject: Evil mode and latex support --- elpa/auctex-13.1.3/font-latex.el | 2314 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 2314 insertions(+) create mode 100644 elpa/auctex-13.1.3/font-latex.el (limited to 'elpa/auctex-13.1.3/font-latex.el') diff --git a/elpa/auctex-13.1.3/font-latex.el b/elpa/auctex-13.1.3/font-latex.el new file mode 100644 index 0000000..c49a698 --- /dev/null +++ b/elpa/auctex-13.1.3/font-latex.el @@ -0,0 +1,2314 @@ +;;; font-latex.el --- LaTeX fontification for Font Lock mode. -*- lexical-binding: t; -*- + +;; Copyright (C) 1996-2022 Free Software Foundation, Inc. + +;; Authors: Peter S. Galbraith +;; Simon Marshall +;; Maintainer: auctex-devel@gnu.org +;; Created: 06 July 1996 +;; Keywords: tex, wp, faces + +;;; This file is not part of GNU Emacs. + +;; This package is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This package is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; This package enhances font-lock fontification patterns for LaTeX. +;; font-lock mode is a minor mode that causes your comments to be +;; displayed in one face, strings in another, reserved words in +;; another, and so on. + +;;; Code: + +(require 'font-lock) +(require 'tex) + +(eval-when-compile + (require 'cl-lib)) + +(defgroup font-latex nil + "Font-latex text highlighting package." + :prefix "font-latex-" + :group 'faces + :group 'tex + :group 'AUCTeX) + +(defgroup font-latex-keywords nil + "Keywords for highlighting text in font-latex." + :prefix "font-latex-" + :group 'font-latex) + +(defgroup font-latex-highlighting-faces nil + "Faces for highlighting text in font-latex." + :prefix "font-latex-" + :group 'font-latex) + +(defvar font-latex-multiline-boundary 5000 + "Size of region to search for the start or end of a multiline construct.") + +(defvar font-latex-quote-regexp-beg nil + "Regexp used to find quotes.") +(make-variable-buffer-local 'font-latex-quote-regexp-beg) + +(defvar font-latex-quote-list '(("``" "''") ("<<" ">>" french) ("«" "»" french)) + "List of quote specifiers for quotation fontification. + +Each element of the list is either a list consisting of two +strings to be used as opening and closing quotation marks +independently of the value of `font-latex-quotes' or a list with +three elements where the first and second element are strings for +opening and closing quotation marks and the third element being +either the symbol `german' or `french' describing the order of +quotes. + +If `font-latex-quotes' specifies a different state, order of the +added quotes will be reversed for fontification. For example if +'(\"\\\"<\" \"\\\">\" french) is given but `font-latex-quotes' +specifies `german', quotes will be used like \">foo\"< for +fontification.") + +(defvar font-latex-quotes-control nil + "Internal variable for keeping track if `font-latex-quotes' changed.") +(make-variable-buffer-local 'font-latex-quotes-control) + +(defvar font-latex-quotes-internal nil + "Internal variable for tracking outcome of automatic detection. +If automatic detection is not enabled, it is assigned the value +of `font-latex-quotes'.") +(make-variable-buffer-local 'font-latex-quotes-internal) + +(defvar font-latex-quotes-fallback 'french + "Fallback value for `font-latex-quotes' if automatic detection fails.") + +(defvar font-latex-quote-style-list-french + '("french" "frenchb" "frenchle" "frenchpro" "francais" "canadien" + "acadian" "italian") + "List of styles for which French-style quote matching should be activated.") + +(defvar font-latex-quote-style-list-german + '("austrian" "german" "germanb" "naustrian" "ngerman") + "List of styles for which German-style quote matching should be activated.") + +(defcustom font-latex-quotes 'auto + "Whether to fontify << French quotes >> or >>German quotes<<. +Also selects \" versus \">quote\"<. + +If value `auto' is chosen, an attempt is being made in deriving +the type of quotation mark matching from document settings like +the language option supplied to the babel package. + +If nil, quoted content will not be fontified." + :type '(choice (const auto) (const french) (const german) (const nil)) + :group 'font-latex) +(put 'font-latex-quotes 'safe-local-variable + (lambda (x) (memq x '(auto french german nil)))) + +(defun font-latex-add-quotes (quotes) + "Add QUOTES to `font-latex-quote-list'. +QUOTES has to be a list adhering to the format of an element of +`font-latex-quote-list'." + (setq font-latex-quotes-control nil) + (add-to-list (make-local-variable 'font-latex-quote-list) quotes)) + +(defun font-latex-quotes-set-internal () + "Set `font-latex-quotes-internal' according to `font-latex-quotes'. +If `font-latex-quotes' is set to `auto', try to derive the +correct value from document properties." + (setq font-latex-quotes-internal + (if (eq font-latex-quotes 'auto) + (or (when (TeX-elt-of-list-member + font-latex-quote-style-list-french TeX-active-styles) + 'french) + (when (TeX-elt-of-list-member + font-latex-quote-style-list-german TeX-active-styles) + 'german) + font-latex-quotes-fallback) + font-latex-quotes))) +;; Update the value of `font-latex-quotes-internal' when the list of +;; styles changes. +(add-hook 'TeX-update-style-hook #'font-latex-quotes-set-internal) + +;; The definitions of the title faces were originally taken from +;; info.el (Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 97, 98, 99, +;; 2000, 2001 Free Software Foundation, Inc.) and adapted to the needs +;; of font-latex.el. + +(defconst font-latex-sectioning-max 5 + "Highest number for font-latex-sectioning-N-face") +(defface font-latex-sectioning-5-face + '((((type tty pc) (class color) (background light)) + (:foreground "blue4" :weight bold)) + (((type tty pc) (class color) (background dark)) + (:foreground "yellow" :weight bold)) + (((class color) (background light)) + (:weight bold :inherit variable-pitch :foreground "blue4")) + (((class color) (background dark)) + (:weight bold :inherit variable-pitch :foreground "yellow")) + (t (:weight bold :inherit variable-pitch))) + "Face for sectioning commands at level 5." + :group 'font-latex-highlighting-faces) + +(defcustom font-latex-fontify-sectioning 1.1 + "Whether to fontify sectioning macros with varying height or a color face. + +If it is a number, use varying height faces. The number is used +for scaling starting from `font-latex-sectioning-5-face'. Typically +values from 1.05 to 1.3 give best results, depending on your font +setup. If it is the symbol `color', use `font-lock-type-face'. + +Caveats: Customizing the scaling factor applies to all sectioning +faces unless those face have been saved by customize. Setting +this variable directly does not take effect unless you call +`font-latex-update-sectioning-faces' or restart Emacs. + +Switching from `color' to a number or vice versa does not take +effect unless you call \\[font-lock-fontify-buffer] or restart +Emacs." + :type '(choice (number :tag "Scale factor") + (const color)) + :initialize #'custom-initialize-default + :set (lambda (symbol value) + (set-default symbol value) + (unless (eq value 'color) + (font-latex-update-sectioning-faces font-latex-sectioning-max value))) + :group 'font-latex) + +(defun font-latex-update-sectioning-faces (&optional max height-scale) + "Update sectioning commands faces." + (unless height-scale + (setq height-scale (if (numberp font-latex-fontify-sectioning) + ;; Make sure `height-scale' is a floating point + ;; number because `set-face-attribute' treats + ;; integers differently from floating points. + (float font-latex-fontify-sectioning) + 1.1))) + (unless max + (setq max font-latex-sectioning-max)) + (dotimes (num max) + (let* (;; reverse for XEmacs: + (num (- max (1+ num))) + (face-name (intern (format "font-latex-sectioning-%s-face" num)))) + (unless (get face-name 'saved-face) ; Do not touch customized faces. + (set-face-attribute face-name nil :height height-scale))))) + +(defun font-latex-make-sectioning-faces (max &optional height-scale) + "Build the faces used to fontify sectioning commands." + (unless max (setq max font-latex-sectioning-max)) + (unless height-scale + (setq height-scale (if (numberp font-latex-fontify-sectioning) + ;; Make sure `height-scale' is a floating point + ;; number because the integer type is treated + ;; differently. + (float font-latex-fontify-sectioning) + 1.1))) + (dotimes (num max) + (let* ((num (- max (1+ num))) + (face-name (intern (format "font-latex-sectioning-%s-face" num))) + (f-inherit (intern (format "font-latex-sectioning-%s-face" (1+ num))))) + (eval + `(defface ,face-name + '((t (:height ,height-scale :inherit ,f-inherit))) + (format "Face for sectioning commands at level %s. + +Probably you don't want to customize this face directly. Better +change the base face `font-latex-sectioning-5-face' or customize the +variable `font-latex-fontify-sectioning'." ',num) + :group 'font-latex-highlighting-faces) + t)))) + +(font-latex-make-sectioning-faces font-latex-sectioning-max) + + +;;; Keywords + +(eval-and-compile +(defconst font-latex-built-in-keyword-classes + '(("warning" + ("nopagebreak" "pagebreak" "newpage" "clearpage" "cleardoublepage" + "enlargethispage" "nolinebreak" "linebreak" "newline" "-" "\\" "\\*" + "appendix" "displaybreak" "allowdisplaybreaks" "tabularnewline" "include" + "backmatter" "frontmatter" "mainmatter" + "makeatletter" "makeatother" "newblock" "suppressfloats" "endinput") + font-latex-warning-face 1 noarg) + ("variable" + (("setlength" "|{\\{") ("settowidth" "|{\\{") ("settoheight" "{{") + ("settodepth" "{{") ("setcounter" "{|{\\") + ("addtolength" "|{\\{") ("addtocounter" "{|{\\") + ("stepcounter" "{") ("refstepcounter" "{") + ("counterwithin" "*[{{") ("counterwithout" "*[{{") + ("arabic" "{") ("roman" "{") ("Roman" "{") ("alph" "{") ("Alph" "{") + ("fnsymbol" "{")) + font-lock-variable-name-face 2 command) + ("biblatexnoarg" + ("newrefsegment" "mancite" "pno" "ppno" "nopp" "psq" "psqq") + font-lock-variable-name-face 2 noarg) + ("biblatex" + (;; 3.2.2 Setting Package Options + ("ExecuteBibliographyOptions" "[{") + ;; 3.7.1 Resources + ("addbibresource" "[{") ("addglobalbib" "[{") ("addsectionbib" "[{") + ;; 3.7.2 The Bibliography + ("printbibliography" "[") ("bibbysection" "[") ("bibbysegment" "[") + ("bibbycategory" "[") ("printbibheading" "[") + ;; 3.7.3 Bibliography Lists + ("printbiblist" "[{") ("printshorthands" "[") + ;; 3.7.4 Bibliography Sections + ("newrefsection" "[") + ;; 3.7.6 Bibliography Categories + ("DeclareBibliographyCategory" "{") ("addtocategory" "{{") + ;; 3.7.7 Bibliography Headings and Environments + ("defbibenvironment" "{{{{") ("defbibheading" "{[{") + ;; 3.7.8 Bibliography Notes + ("defbibnote" "{{") + ;; 3.7.9 Bibliography Filters and Checks + ("defbibfilter" "{{") ("defbibcheck" "{{") + ;; 3.7.10 Reference Contexts + ("DeclareRefcontext" "{{") ("newrefcontext" "[{") + ("assignrefcontextkeyws" "*[{") ("assignrefcontextcats" "*[{") + ("assignrefcontextentries" "*[{") + ;; 3.7.11 Dynamic Entry Sets + ("defbibentryset" "{{") + ;; 3.8.1 Standard Commands + ("Cite" "[[{") + ("parencite" "*[[{") ("Parencite" "[[{") + ("footcite" "[[{") ("footcitetext" "[[{") + ;; 3.8.2 Style-specific Commands + ("textcite" "[[{") ("Textcite" "[[{") + ("smartcite" "[[{") ("Smartcite" "[[{") + ("supercite" "{") + ;; 3.8.3 Qualified Citation Lists + ;; For qualified lists, fontify at least 2 mandatory arguments + ("cites" "(([[{[[{") ("Cites" "(([[{[[{") + ("parencites" "(([[{[[{") ("Parencites" "(([[{[[{") + ("footcites" "(([[{[[{") ("footcitetexts" "(([[{[[{") + ("smartcites" "(([[{[[{") ("Smartcites" "(([[{[[{") + ("textcites" "(([[{[[{") ("Textcites" "(([[{[[{") + ("supercites" "(([[{[[{") + ;; 3.8.4 Style-independent Commands + ("autocite" "*[[{") ("Autocite" "*[[{") + ("autocites" "(([[{[[{") ("Autocites" "(([[{[[{") + ;; 3.8.5 Text Commands + ("citeauthor" "*[[{") ("Citeauthor" "*[[{") ("citetitle" "*[[{") + ("citeyear" "*[[{") ("citedate" "*[[{") + ("citeurl" "[[{") ("parentext" "{") + ("brackettext" "{") + ;; 3.8.6 Special Commands + ("fullcite" "[[{") ("footfullcite" "[[{") + ("volcite" "[{[{") ("Volcite" "[{[{") + ("volcites" "(([{[{[{[{") ("Volcites" "(([{[{[{[{") + ("pvolcite" "[{[{") ("Pvolcite" "[{[{") + ("pvolcites" "(([{[{[{[{") ("Pvolcites" "(([{[{[{[{") + ("fvolcite" "[{[{") ("ftvolcite" "[{[{") + ("fvolcites" "(([{[{[{[{") ("Fvolcites" "(([{[{[{[{") + ("svolcite" "[{[{") ("Svolcite" "[{[{") + ("svolcites" "(([{[{[{[{") ("Svolcites" "(([{[{[{[{") + ("tvolcite" "[{[{") ("Tvolcite" "[{[{") + ("tvolcites" "(([{[{[{[{") ("Tvolcites" "(([{[{[{[{") + ("avolcite" "[{[{") ("Avolcite" "[{[{") + ("avolcites" "(([{[{[{[{") ("Avolcites" "(([{[{[{[{") + ("notecite" "[[{") ("Notecite" "[[{") + ("pnotecite" "[[{") ("Pnotecite" "[[{") + ("fnotecite" "[[{") + ;; 3.8.7 Low-level Commands + ("citename" "[[{[{") ("citelist" "[[{[{") ("citefield" "[[{[{") + ;; 3.8.8 Miscellaneous Commands + ("citereset" "*") ("RN" "{") ("Rn" "{") + ;; 3.9 Localization Commands + ("DefineBibliographyStrings" "{{") ("DefineBibliographyExtras" "{{") + ("UndefineBibliographyExtras" "{{") ("DefineHyphenationExceptions" "{{") + ("NewBibliographyString" "{")) + font-lock-constant-face 2 command) + ("reference" + (("nocite" "*{") ("cite" "*[[{") ("label" "{") ("pageref" "{") + ("vref" "*{") ("eqref" "{") ("ref" "{") ("Ref" "{") + ("footref" "{") ("include" "{") ("input" "{") + ("bibliography" "{") ("index" "{") ("glossary" "{") + ("footnote" "[{") ("footnotemark" "[") ("footnotetext" "[{") + ("marginpar" "[{") ("chaptermark" "{") ("sectionmark" "{") + ("subsectionmark" "{") ("subsubsectionmark" "{") + ("paragraphmark" "{") ("subparagraphmark" "{")) + font-lock-constant-face 2 command) + ("function" + (("begin" "{") ("end" "{") ("pagenumbering" "{") + ("thispagestyle" "{") ("pagestyle" "{") ("nofiles" "") + ("includeonly" "{") ("bibliographystyle" "{") ("documentstyle" "[{") + ("documentclass" "[{[") ("newenvironment" "*{[[{{") + ("newcommand" "*|{\\[[{") ("newlength" "|{\\") + ("newtheorem" "{[{[") + ("providecommand" "*|{\\[[{") + ("newcounter" "{[") ("renewenvironment" "*{[[{{") + ("renewcommand" "*|{\\[[{") ("renewtheorem" "{[{[") + ("usepackage" "[{[") ("RequirePackage" "[{[") + ("fbox" "{") ("mbox" "{") ("rule" "[{{") + ("framebox" "|[([{") ("makebox" "|[([{") ("newsavebox" "|{\\") + ("parbox" "[[[{{") ("savebox" "|{\\|[([{") ("sbox" "|{\\{") + ("usebox" "|{\\") + ("cline" "{") ("extracolsep" "{") ("multicolumn" "{{{") + ("linethickness" "{") ("multiput" "(({{") ("put" "({") + ("qbezier" "[(((") ("raisebox" "{[[{") + ("addvspace" "{") ("vspace" "*{") ("hspace" "*{") + ("addcontentsline" "{{{") ("addtocontents" "{{") + ("labelformat" "{{") + ("AddToHook" "{[{") ("RemoveFromHook" "{[") ("AddToHookNext" "{{") + ("ProvidesClass" "{[") ("ProvidesPackage" "{[") ("ProvidesFile" "{[") + ;; XXX: Should macros without arguments rather be listed in a + ;; separate category with 'noarg instead of 'command handling? + ("enspace" "") ("enskip" "") ("quad" "") ("qquad" "") ("nonumber" "") + ("bigskip" "") ("medskip" "") ("smallskip" "") + ("thinspace" "") ("negthinspace" "") + ("thicklines" "") ("thinlines" "") + ("noindent" "") ("hline" "") ("ldots" "") + ("centering" "") ("raggedright" "") ("raggedleft" "") + ("TeX" "") ("LaTeX" "") ("LaTeXe" "") + ("normalfont" "") ("normalshape" "") + ("tableofcontents" "") ("listoffigures" "") ("listoftables" "")) + font-lock-function-name-face 2 command) + ("sectioning-0" + (("part" "*[{")) + (if (eq font-latex-fontify-sectioning 'color) + 'font-lock-type-face + 'font-latex-sectioning-0-face) + 2 command) + ("sectioning-1" + (("chapter" "*[{")) + (if (eq font-latex-fontify-sectioning 'color) + 'font-lock-type-face + 'font-latex-sectioning-1-face) + 2 command) + ("sectioning-2" + (("section" "*[{")) + (if (eq font-latex-fontify-sectioning 'color) + 'font-lock-type-face + 'font-latex-sectioning-2-face) + 2 command) + ("sectioning-3" + (("subsection" "*[{")) + (if (eq font-latex-fontify-sectioning 'color) + 'font-lock-type-face + 'font-latex-sectioning-3-face) + 2 command) + ("sectioning-4" + (("subsubsection" "*[{")) + (if (eq font-latex-fontify-sectioning 'color) + 'font-lock-type-face + 'font-latex-sectioning-4-face) + 2 command) + ("sectioning-5" + (("paragraph" "*[{") ("subparagraph" "*[{") + ("subsubparagraph" "*[{")) + (if (eq font-latex-fontify-sectioning 'color) + 'font-lock-type-face + 'font-latex-sectioning-5-face) + 2 command) + ("slide-title" () font-latex-slide-title-face 2 command) + ("textual" + (("item" "[") ("bibitem" "[{") ("title" "{") ("author" "{") ("date" "{") + ("thanks" "{") ("address" "{") ("caption" "[{") + ("textsuperscript" "{") ("textsubscript" "{") ("verb" "*")) + font-lock-type-face 2 command) + ("bold-command" + (("textbf" "{") ("textsc" "{") ("textssc" "{") ("textulc" "{") + ("textup" "{") ("textsw" "{") ("boldsymbol" "{") ("pmb" "{") + ("mathbf" "{")) + font-latex-bold-face 1 command) + ("italic-command" + (("emph" "{") ("textit" "{") ("textsl" "{") ("mathit" "{")) + font-latex-italic-face 1 command) + ("math-command" + (("ensuremath" "|{\\")) + font-latex-math-face 1 command) + ("type-command" + (("texttt" "{") ("textsf" "{") ("textrm" "{") ("textmd" "{") + ("textnormal" "{") ("oldstylenums" "{") ("legacyoldstylenums" "{") + ("mathrm" "{") ("mathsf" "{") ("mathtt" "{")) + font-lock-type-face 1 command) + ("bold-declaration" + ("bf" "bfseries" "sc" "scshape" "sscshape" "ulcshape" "upshape" "swshape") + font-latex-bold-face 1 declaration) + ("italic-declaration" + ("em" "it" "itshape" "sl" "slshape") + font-latex-italic-face 1 declaration) + ("type-declaration" + ("tt" "ttfamily" "sf" "sffamily" "rm" "rmfamily" "mdseries" + "tiny" "scriptsize" "footnotesize" "small" "normalsize" + "large" "Large" "LARGE" "huge" "Huge") + font-lock-type-face 1 declaration)) + "Built-in keywords and specifications for font locking. + +The first element of each item is the name of the keyword class. + +The second element is a list of keywords (macros without an +escape character) to highlight or, if the fifth element is the +symbol `command', a list of lists where the first element of each +item is a keyword and the second a string specifying the macro +syntax. It can contain \"*\" if the macro has a starred variant, +\"[\" for an optional argument, \"{\" for a mandatory argument, +and \"\\\" for a macro. A \"|\" means the following two tokens +should be regarded as alternatives. + +The third element is the symbol of a face to be used or a Lisp +form returning a face symbol. + +The fourth element is the fontification level. + +The fifth element is the type of construct to be matched. It can +be one of `noarg' which will match simple macros without +arguments (like \"\\foo\"), `declaration' which will match macros +inside a TeX group (like \"{\\bfseries foo}\"), or `command' which +will match macros of the form \"\\foo[bar]{baz}\".")) + +(defcustom font-latex-deactivated-keyword-classes nil + "List of strings for built-in keyword classes to be deactivated. + +Valid entries are \"warning\", \"variable\", \"biblatexnoarg\", +\"biblatex\", \"reference\", \"function\" , \"sectioning-0\", +\"sectioning-1\", \"sectioning-2\", \"sectioning-3\", +\"sectioning-4\", \"sectioning-5\", \"slide-title\", \"textual\", +\"bold-command\", \"italic-command\", \"math-command\", +\"type-command\", \"bold-declaration\", \"italic-declaration\", +\"type-declaration\". + +You have to restart Emacs for a change of this variable to take effect." + :group 'font-latex-keywords + :type `(set ,@(mapcar + (lambda (spec) + `(const :tag ,(concat + ;; Name of the keyword class + (let ((name (split-string (car spec) "-"))) + (setcar name (capitalize (car name))) + (mapconcat #'identity name " ")) + " keywords in `" + ;; Name of the face + (symbol-name + (let ((face (nth 2 spec))) + (if (symbolp face) face (eval face t)))) + "'.\n" + ;; List of keywords + (with-temp-buffer + (insert " Keywords: " + (mapconcat (lambda (x) + (if (listp x) + (car x) + x)) + (nth 1 spec) ", ")) + (fill-paragraph nil) + (buffer-string))) + ,(car spec))) + font-latex-built-in-keyword-classes))) + +(eval-and-compile +(defun font-latex--make-match-defun (prefix name face type) + "Return a function definition for keyword matching. +The variable holding the keywords to match are determined by the +strings PREFIX and NAME. The type of matcher is determined by +the symbol TYPE. + +This is a helper function for `font-latex-make-built-in-keywords' +and `font-latex-make-user-keywords' and not intended for general +use." + ;; FIXME: Is the cond-clause possible inside of the defun? + + ;; In an earlier version of font-latex the type could be a list like + ;; (command 1). This indicated a macro with one argument. Provide + ;; a match function in this case but don't actually support it. + (cond ((or (eq type 'command) (listp type)) + `(defun ,(intern (concat prefix name)) (limit) + ,(concat "Fontify `" prefix name "' up to LIMIT. + +Generated by `font-latex--make-match-defun'.") + (when ,(intern (concat prefix name)) + (font-latex-match-command-with-arguments + ,(intern (concat prefix name)) + (append + (when (boundp ',(intern (concat prefix name + "-keywords-local"))) + ,(intern (concat prefix name "-keywords-local"))) + ,(intern (concat prefix name "-keywords"))) + ;; `face' can be a face symbol, a form returning + ;; a face symbol, or a list of face attributes. + ,(if (and (listp face) (fboundp (car face))) + face + `',face) + limit)))) + ((eq type 'declaration) + `(defun ,(intern (concat prefix name)) (limit) + ,(concat "Fontify `" prefix name "' up to LIMIT. + +Generated by `font-latex--make-match-defun'.") + (when ,(intern (concat prefix name)) + (font-latex-match-command-in-braces + ,(intern (concat prefix name)) limit)))) + ((eq type 'noarg) + `(defun ,(intern (concat prefix name)) (limit) + ,(concat "Fontify `" prefix name "' up to LIMIT. + +Generated by `font-latex--make-match-defun'.") + (when ,(intern (concat prefix name)) + (re-search-forward + ,(intern (concat prefix name)) limit t)))))) + +(defun font-latex-keyword-matcher (prefix name face type) + "Return a matcher and highlighter as required by `font-lock-keywords'. +PREFIX and NAME are strings which are concatenated to form the +respective match function. FACE is a face name or a list of face +attributes that will be applied to the respective part of the +match returned by the match function. A lisp form returning a +face name or a list of face attributes is also valid for FACE. +TYPE is the type of construct to be highlighted. Currently the +symbols `command', `declaration' and `noarg' are valid. + +This is a helper function for `font-latex-make-built-in-keywords' +and `font-latex-make-user-keywords' and not intended for general +use." + ;; Quote a list of face attributes and a face symbol + ;; but do not quote a form returning such value. + (unless (and (listp face) (fboundp (car face))) + (setq face `',face)) + + ;; In an earlier version of font-latex the type could be a list like + ;; (command 1). This indicated a macro with one argument. Provide + ;; a matcher in this case but don't actually support it. + (cond ((or (eq type 'command) (listp type)) + `(,(intern (concat prefix name)) + (0 (font-latex-matched-face 0) append t) + (1 (font-latex-matched-face 1) append t) + (2 (font-latex-matched-face 2) append t) + (3 (font-latex-matched-face 3) append t) + (4 (font-latex-matched-face 4) append t) + (5 (font-latex-matched-face 5) append t) + (6 (font-latex-matched-face 6) append t) + (7 (font-latex-matched-face 7) append t) + (8 (font-latex-matched-face 8) append t) + (9 (font-latex-matched-face 9) append t) + (10 (font-latex-matched-face 10) append t) + (11 (font-latex-matched-face 11) append t))) + ((eq type 'noarg) + `(,(intern (concat prefix name)) + (0 ,face))) + ((eq type 'declaration) + `(,(intern (concat prefix name)) + (0 'font-latex-warning-face t t) + (1 'font-lock-keyword-face append t) + (2 ,face append t)))))) + +(defmacro font-latex-make-built-in-keywords () + "Build defuns, defvars and defcustoms for built-in keyword fontification." + (let ((flks '()) + (defs '())) + (dolist (item font-latex-built-in-keyword-classes) + (let ((prefix "font-latex-match-") + (name (nth 0 item)) + (keywords (nth 1 item)) + (face (nth 2 item)) + (level (nth 3 item)) + (type (nth 4 item))) + + ;; defvar font-latex-match-*-keywords-local + (push `(defvar-local ,(intern (concat prefix name "-keywords-local")) + ',keywords + ,(concat "Buffer-local keywords to add to `" + prefix name "-keywords'.\n\n" + (if (eq type 'command) + "\ +This must be a list where each element is a list consisting of a +keyword string \(not a regular expression\) omitting the leading +backslash and a format specifier as described in the doc string of +`font-latex-user-keyword-classes'." + "\ +This must be a list where each element is a keyword string \(not a +regular expression\) omitting the leading backslash.") + + "\n\n\ +This is an internal variable which should not be set directly. +Use `font-latex-add-keywords' instead. + +Generated by `font-latex-make-built-in-keywords'.")) + defs) + + ;; defvar font-latex-match-* + ;; We make this variable buffer local later, but don't use + ;; `defvar-local' here because it shouldn't have nil as its + ;; default value. Its true default value is set by + ;; through font-latex-match-*-make in :set specification of + ;; defcustom of font-latex-match-*-keywords below. It's + ;; only after that this variable can be buffer local. + (push `(defvar ,(intern (concat prefix name)) nil + ,(concat "Regular expression to match " name + " keywords. + +Generated by `font-latex-make-built-in-keywords'")) + defs) + + ;; This defvar (without value) is here just to suppress compiler + ;; warnings. Its true definition is done by defcustom following + ;; the next defun because its :set function depends on the + ;; function defined by that defun. + (push `(defvar ,(intern (concat prefix name "-keywords"))) + defs) + + ;; defun font-latex-match-*-make + (push `(defun ,(intern (concat prefix name "-make")) () + ,(concat "Make or remake the variable `" prefix name "'. + +Generated by `font-latex-make-built-in-keywords'.") + (let ((keywords + (append + (unless (member ,name + font-latex-deactivated-keyword-classes) + ,(intern (concat prefix name "-keywords-local"))) + ,(intern (concat prefix name "-keywords")))) + multi-char-macros single-char-macros) + (dolist (elt keywords) + (let ((keyword (if (listp elt) (car elt) elt))) + (if (string-match "^[A-Za-z]" keyword) + (push keyword multi-char-macros) + (push keyword single-char-macros)))) + (when (or multi-char-macros single-char-macros) + (setq ,(intern (concat prefix name)) + (concat + "\\\\\\(" + (when multi-char-macros + (concat + "\\(?:" (regexp-opt multi-char-macros) "\\)\\>")) + (when single-char-macros + (concat + (when multi-char-macros "\\|") + "\\(?:" (regexp-opt single-char-macros) "\\)")) + "\\)"))))) + defs) + + ;; defcustom font-latex-match-*-keywords + (push `(defcustom ,(intern (concat prefix name "-keywords")) nil + ,(concat "List of keywords " + (when (eq type 'command) "and formats ") + "for " name " face.\n" + (if (eq type 'command) + "\ +Each element has to be a list consisting of the name of a macro +omitting the leading backslash and a format specifier as +described in the doc string of `font-latex-user-keyword-classes'." + "\ +Each element has to be the name of a macro as a string, omitting +the leading backslash.") + "\n\n\ +Setting this variable directly does not take effect; restart +Emacs. + +Generated by `font-latex-make-built-in-keywords'.") + :type '(repeat ,(if (eq type 'command) + '(list (string :tag "Keyword") + (string :tag "Format")) + '(string :tag "Keyword"))) + :set (lambda (symbol value) + (set-default symbol value) + (funcall ',(intern (concat prefix name "-make")))) + :group 'font-latex-keywords) + defs) + + ;; Now that font-latex-match-* has attained proper default + ;; value, make it buffer local. + (push `(make-variable-buffer-local ',(intern (concat prefix name))) + defs) + + ;; defun font-latex-match-* + (push (font-latex--make-match-defun prefix name face type) defs) + + ;; Add matchers and highlighters to `font-latex-keywords-{1,2}'. + (let ((keywords-entry (font-latex-keyword-matcher + prefix name face type))) + (push (cons level keywords-entry) flks)))) + `(progn + ,@(nreverse defs) + (defvar font-latex-keywords-1 + ',(nreverse (delq nil (mapcar (lambda (x) (if (eq 1 (car x)) (cdr x))) + flks))) + "Subdued level highlighting for LaTeX modes.") + (defvar font-latex-keywords-2 + ',(nreverse (mapcar #'cdr flks)) + "High level highlighting for LaTeX modes.")))) + +(font-latex-make-built-in-keywords) + +(defcustom font-latex-user-keyword-classes nil + "List of user-defined keyword classes for font locking. + +Every keyword class consists of four parts, a name, a list of +keywords, a face and a specifier for the type of macro to be +highlighted. + +When adding new entries, you have to use unique values for the +class names, that is, they must not clash with names of the +built-in keyword classes or other names given by you. +Additionally the names must not contain spaces. + +The list of keywords defines which commands and declarations +should be covered by the keyword class. A keyword can either be +a simple command name omitting the leading backslash or a list +consisting of the command name and a string specifying the syntax +of the command. The latter is useful if you want to match LaTeX +macros with arguments (see below). You can specify the occurence +and order of optional (\"[\") and mandatory (\"{\") arguments for +each keyword. For example for \"documentclass\" you'd use \"[{\" +because the macro has one optional followed by one mandatory +argument. Optionally starred macros can be indicated with \"*\". +In case an argument is an unbraced macro, use \"\\\". You can +also specify two alternative arguments by prefixing them with +\"|\". As an example, the specifier for \\newcommand is +\"*|{\\=\\[[{\". + +The face argument can either be an existing face or a face +attribute. + +There are three alternatives for the class type: + +A value of `command' indicates commands with arguments +\(\"\\foo[bar]{baz}\"). The mandatory arguments in curly braces +will get the face you specified. + +A value of `declaration' indicates declarations inside of TeX +groups (\"{\\foo bar}\"). The content inside the braces, +excluding the command, will get the face you specified. In case +the braces are missing, the face will be applied to the command +itself. + +A value of `noarg' indicates commands without arguments +\(\"\\foo\"). The command itself will get the face you +specified. + +Setting this variable directly does not take effect; +restart Emacs." + :group 'font-latex-keywords + :type '(repeat (list (string :tag "Name") + (choice (repeat :tag "Keywords" (string :tag "Keyword")) + (repeat + :tag "Keywords with specs" + (group (string :tag "Keyword") + (string :tag "Format specifier")))) + (choice (face :tag "Face name") + (custom-face-edit :tag "Face attributes")) + (choice :tag "Type" + ;; Maps to + ;;`font-latex-match-command-with-arguments' + (const :tag "Command with arguments" + command) + ;; Maps to + ;;`font-latex-match-command-in-braces' + (const :tag "Declaration inside TeX group" + declaration) + ;; Maps to `re-search-forward' + (const :tag "Command without arguments" + noarg)))) + :set (lambda (symbol value) + (dolist (item value) + (when (string-match " " (car item)) + (error "No spaces allowed in name"))) + (let (names names-uniq) + (dolist (item (append font-latex-built-in-keyword-classes value)) + (setq names (append names (list (car item))))) + (setq names (TeX-sort-strings names)) + (setq names-uniq (TeX-delete-duplicate-strings names)) + (dotimes (i (safe-length names-uniq)) + (unless (string= (nth i names) (nth i names-uniq)) + (error "Name %S already exists" (nth i names))))) + (set-default symbol value) + (let ((prefix "font-latex-match-")) + (dolist (elt value) + (unless (boundp (intern (concat prefix (car elt)))) + ;; defvar font-latex-match-* + (eval `(defvar ,(intern (concat prefix (car elt))) nil + ,(concat "Regular expression to match " (car elt) + " keywords. + +Generated by `font-latex-user-keyword-classes'")))) + (let ((keywords (nth 1 elt)) + single-char-macro-flag) + (setq keywords (if (listp (car keywords)) + (mapcar #'car keywords) + keywords)) + (catch 'single-char + (dolist (keyword keywords) + (unless (string-match "^[A-Za-z]" keyword) + (setq single-char-macro-flag t) + (throw 'single-char nil)))) + (set (intern (concat prefix (car elt))) + (when (> (safe-length keywords) 0) + (concat "\\\\" (let ((max-specpdl-size 1000)) + (regexp-opt keywords t)) + (unless single-char-macro-flag "\\>"))))))))) + +(defun font-latex-make-user-keywords () + "Build defuns and defvars for user keyword fontification." + (let ((keyword-specs font-latex-user-keyword-classes)) + (dolist (item keyword-specs) + (let ((prefix "font-latex-match-") + (name (nth 0 item)) + (keywords (nth 1 item)) + (face (nth 2 item)) + (type (nth 3 item))) + + ;; defvar font-latex-match-*-keywords + (eval `(defvar ,(intern (concat prefix name "-keywords")) ',keywords + ,(concat "Font-latex keywords for " name " face. + +Generated by `font-latex-make-user-keywords'."))) + + ;; defun font-latex-match-* + (eval (font-latex--make-match-defun prefix name face type) t) + + ;; Add the matcher to `font-latex-keywords-2'. + (add-to-list 'font-latex-keywords-2 + (font-latex-keyword-matcher prefix name face type) t)))) + + ;; Add the "fixed" matchers and highlighters. + (dolist (item + '(("\\(^\\|[^\\]\\)\\(&+\\)" 2 'font-latex-warning-face) + (font-latex-match-dollar-math 0 'font-latex-math-face keep) + (font-latex-match-quotation + (0 'font-latex-string-face append) + (1 'font-latex-warning-face)) + ;; Hack to remove the verbatim face from the \ in + ;; \end{verbatim} and similar. The same hack is used in + ;; tex-mode.el. + ("\\(\\\\\\)end" + (1 (get-text-property (match-end 1) 'face) t)))) + (add-to-list 'font-latex-keywords-1 item) + (add-to-list 'font-latex-keywords-2 item)) + (dolist (item + '((font-latex-match-math-env + (0 'font-latex-warning-face t t) + (1 'font-latex-math-face append t)) + (font-latex-match-math-envII + (1 'font-latex-math-face append t)) + (font-latex-match-simple-command + (0 'font-latex-sedate-face append)) + (font-latex-match-script + (1 (font-latex-script (match-beginning 0)) append)) + (font-latex-match-script-chars + (1 (font-latex-script-char (match-beginning 1)) prepend)))) + (add-to-list 'font-latex-keywords-2 item t))) +(font-latex-make-user-keywords) + +(defun font-latex-add-keywords (keywords class) + "Add KEYWORDS to CLASS. +KEYWORDS is a list of keywords or keywords with syntax specs. +CLASS corresponds to a keyword class and can be one of the +symbols `warning', `variable', `reference', `biblatexnoarg', +`biblatex', `function', `sectioning-0', `sectioning-1', +`sectioning-2', `sectioning-3', `sectioning-4', `sectioning-5', +`slide-title', `textual', `bold-command', `italic-command', +`math-command', `type-command', `bold-declaration', +`italic-declaration' or `type-declaration'. + +The keywords will be added to the buffer-local list of keywords +of the respective keyword class and necessary updates of the font +locking machinery will be triggered." + (let* ((class (symbol-name class)) + (list (intern (format "font-latex-match-%s-keywords-local" class)))) + (dolist (elt keywords) + (add-to-list list elt)) + (funcall (intern (format "font-latex-match-%s-make" class))) + ;; Trigger refontification. + (when (fboundp 'font-lock-flush) + (font-lock-flush)))) + +(defvar font-latex-keywords font-latex-keywords-1 + "Default expressions to highlight in TeX mode.") + + +;;; Subscript and superscript + +(defcustom font-latex-fontify-script t + "If non-nil, fontify subscript and superscript strings. + +By default, super/subscripts are raised/lowered if this variable +is non-nil. This fontification only affects one level of +scripts, for example in x^{y^z}, the y and the z have the same +size and are equally raised over x. + +If this variable is set to the symbol `multi-level', then y is +raised above x, and z is raised above y. With many script +levels, the text might become too small to be readable, thus +there is the option `font-latex-fontify-script-max-level'. (The +factors for text shrinking are defined in the faces +`font-latex-superscript-face' and `font-latex-subscript-face' and +the raise/lower factor in `font-latex-script-display'.) + +If this variable is set to the symbol `invisible', then the +effect is essentially like `multi-level' but additionally the +script operators ^ and _ are not displayed." + :type '(choice (boolean :tag "Enabled") + (const :tag "Multiple levels" multi-level) + (const :tag "Hide ^ and _" invisible)) + :group 'font-latex) +(put 'font-latex-fontify-script 'safe-local-variable + (lambda (val) + (or (booleanp val) + (memq val '(multi-level invisible))))) + +(defcustom font-latex-fontify-script-max-level 3 + "Maximum scriptification level for which script faces are applied. +The faces `font-latex-superscript-face' and +`font-latex-subscript-face' define custom :height values < 1.0. +Therefore, scripts are displayed with a slightly smaller font +than normal math text. If `font-latex-fontify-script' is +`multi-level' or `invisible', the font size becomes too small to +be readable after a few levels. This option allows to specify +the maximum level after which the size of the script text won't +be shrunken anymore. + +For example, see this expression: + + \\( x^{y^{z^a_b}} \\) + +x has scriptification level 0, y has level 1, z has level 2, and +both a and b have scriptification level 3. + +If `font-latex-fontify-script-max-level' was 2, then z, a, and b +would have the same font size. If it was 3 or more, then a and b +were smaller than z just in the same way as z is smaller than y +and y is smaller than x." + :group 'font-latex + :type 'integer) + +(defcustom font-latex-script-display '((raise -0.5) . (raise 0.5)) + "Display specification for subscript and superscript content. +The car is used for subscript, the cdr is used for superscripts." + :group 'font-latex + :type '(cons (choice (sexp :tag "Subscript form") + (const :tag "No lowering" nil)) + (choice (sexp :tag "Superscript form") + (const :tag "No raising" nil)))) + + +;;; Syntactic keywords + +(defvar font-latex-syntactic-keywords nil + "Syntactic keywords used by `font-latex'.") +(make-variable-buffer-local 'font-latex-syntactic-keywords) + +(defvar font-latex-syntactic-keywords-extra nil + "List of syntactic keywords to add to `font-latex-syntactic-keywords'. +The form should be the same as in `font-lock-syntactic-keywords'.") +(make-variable-buffer-local 'font-latex-syntactic-keywords-extra) + +;; Set and updated in `font-latex-set-syntactic-keywords'. +(defvar font-latex-doctex-syntactic-keywords nil) + +(defun font-latex-set-syntactic-keywords () + "Set the variable `font-latex-syntactic-keywords'. +This function can be used to refresh the variable in case other +variables influencing its value, like `LaTeX-verbatim-environments', +have changed." + ;; Checks for non-emptiness of lists added in order to cater for + ;; installations where `(regexp-opt-group nil)' would enter a loop. + (let ((verb-envs (and (fboundp 'LaTeX-verbatim-environments) + (LaTeX-verbatim-environments))) + (verb-macros-with-delims + (and (fboundp 'LaTeX-verbatim-macros-with-delims) + (LaTeX-verbatim-macros-with-delims))) + (verb-macros-with-braces + (and (fboundp 'LaTeX-verbatim-macros-with-braces) + (LaTeX-verbatim-macros-with-braces)))) + (setq verb-envs (and verb-envs (regexp-opt verb-envs)) + verb-macros-with-delims (and verb-macros-with-delims + (regexp-opt verb-macros-with-delims)) + verb-macros-with-braces (and verb-macros-with-braces + (regexp-opt verb-macros-with-braces)) + font-latex-syntactic-keywords nil) + (unless (= (length verb-envs) 0) + (add-to-list + 'font-latex-syntactic-keywords + `(,(concat + "^[ \t]*\\\\begin *{\\(?:" verb-envs "\\)}" + ;; Some environments accept an optional and/or mandatory + ;; argument that can span over more lines. Between + ;; "\begin{}" and the optional argument there can + ;; be whitespaces and the newline can be commented by a "%" + ;; character. + "[ \t]*\\(?:%.*\n[ \t]*\\)?" + ;; The following line of the regexp matches the optional + ;; argument and allows for up to one level of brackets + ;; inside the argument (e.g., the dialect of a language in + ;; the `lstlisting' environment by the `listings' package). + "\\(?:\\[[^][]*\\(?:\\[[^][]*\\][^][]*\\)*\\]\\)?" + ;; After the optional argument, there may also be another + ;; mandatory argument(s) (e.g. with VerbatimOut or the + ;; minted envs or defined with `lstnewenvironment'). Use + ;; the same trick as above in order to allow one level of + ;; braces in the argument. + "\\(?:{[^{}]*\\(?:{[^{}]*}[^{}]*\\)*}\\)*" + ;; Now match the final newline. The "." alternative + ;; catches the case where verbatim content is written + ;; immediately after the \begin{verbatim}. + "\\(\n\\|.\\)") + (1 "|" t))) + (add-to-list + 'font-latex-syntactic-keywords + ;; Using the newline character for the syntax property often + ;; resulted in fontification problems when text was inserted at + ;; the end of the verbatim environment. That's why we now use + ;; the starting backslash of \end. There is a hack in + ;; `font-latex-make-user-keywords' to remove the spurious + ;; fontification of the backslash. + `(,(concat "\\(\\\\\\)end *{\\(?:" verb-envs "\\)}") + (1 "|" t)))) + (unless (= (length verb-macros-with-delims) 0) + (add-to-list + 'font-latex-syntactic-keywords + `(,(concat "\\\\\\(?:" verb-macros-with-delims "\\)" + ;; Some macros take an optional argument. This is + ;; the same line as above for environments. + "\\(?:\\[[^][]*\\(?:\\[[^][]*\\][^][]*\\)*\\]\\)?" + ;; An opening curly brace as delimiter is valid, but + ;; allowing it might screw up fontification of stuff + ;; like "\url{...} foo \textbf{<--!...}". + "\\([^a-z@*\n\f{]\\).*?" + ;; Give an escape char at the end of the verbatim + ;; construct punctuation syntax. Prevents wrong + ;; fontification of stuff like "\verb|foo\|". + "\\(" (regexp-quote TeX-esc) "*\\)\\(\\1\\)") + (1 "\"") (2 ".") (3 "\"")))) + (unless (= (length verb-macros-with-braces) 0) + (add-to-list + 'font-latex-syntactic-keywords + `(,(concat "\\\\\\(?:" verb-macros-with-braces "\\)" + ;; Some macros take an optional argument. This is + ;; the same line as above for environments. + "\\(?:\\[[^][]*\\(?:\\[[^][]*\\][^][]*\\)*\\]\\)?" + "\\({\\).*?[^\\]\\(?:\\\\\\\\\\)*\\(}\\)") + (1 "|") (2 "|"))))) + (when font-latex-syntactic-keywords-extra + (nconc font-latex-syntactic-keywords font-latex-syntactic-keywords-extra)) + ;; ;; Cater for docTeX mode. + ;; (setq font-latex-doctex-syntactic-keywords + ;; (append font-latex-syntactic-keywords + ;; ;; For docTeX comment-in-doc. + ;; '(("\\(\\^\\)\\^A" (1 (font-latex-doctex-^^A)))))) + ;; Finally, compute our `syntax-propertize-function' anew. + (setq-local syntax-propertize-function + (font-latex--make-syntax-propertize-function))) + + +;;; Syntactic fontification + +(defun font-latex-syntactic-face-function (state) + (if (nth 3 state) + 'font-latex-verbatim-face + 'font-lock-comment-face)) + +;;; Faces + +(defface font-latex-bold-face + (let ((font '(:inherit bold))) + `((((class grayscale) (background light)) + (:foreground "DimGray" ,@font)) + (((class grayscale) (background dark)) + (:foreground "LightGray" ,@font)) + (((class color) (background light)) + (:foreground "DarkOliveGreen" ,@font)) + (((class color) (background dark)) + (:foreground "OliveDrab" ,@font)) + (t (,@font)))) + "Face used to highlight text to be typeset in bold." + :group 'font-latex-highlighting-faces) + +(defface font-latex-italic-face + (let ((font '(:inherit italic))) + `((((class grayscale) (background light)) + (:foreground "DimGray" ,@font)) + (((class grayscale) (background dark)) + (:foreground "LightGray" ,@font)) + (((class color) (background light)) + (:foreground "DarkOliveGreen" ,@font)) + (((class color) (background dark)) + (:foreground "OliveDrab" ,@font)) + (t (,@font)))) + "Face used to highlight text to be typeset in italic." + :group 'font-latex-highlighting-faces) + +(defface font-latex-math-face + (let ((font '(:inherit underline))) + `((((class grayscale) (background light)) + (:foreground "DimGray" ,@font)) + (((class grayscale) (background dark)) + (:foreground "LightGray" ,@font)) + (((class color) (background light)) + (:foreground "SaddleBrown")) + (((class color) (background dark)) + (:foreground "burlywood")) + (t (,@font)))) + "Face used to highlight math." + :group 'font-latex-highlighting-faces) + +(defface font-latex-sedate-face + '((((class grayscale) (background light)) (:foreground "DimGray")) + (((class grayscale) (background dark)) (:foreground "LightGray")) + (((class color) (background light)) (:foreground "DimGray")) + (((class color) (background dark)) (:foreground "LightGray")) + ;;;(t (:underline t)) + ) + "Face used to highlight sedate stuff." + :group 'font-latex-highlighting-faces) + +(defface font-latex-string-face + (let ((font '(:inherit italic))) + `((((type tty) (class color)) + (:foreground "green")) + (((class grayscale) (background light)) + (:foreground "DimGray" ,@font)) + (((class grayscale) (background dark)) + (:foreground "LightGray" ,@font)) + (((class color) (background light)) + (:foreground "RosyBrown")) + (((class color) (background dark)) + (:foreground "LightSalmon")) + (t (,@font)))) + "Face used to highlight strings." + :group 'font-latex-highlighting-faces) + +(defface font-latex-warning-face + (let ((font '(:inherit bold))) + `((((class grayscale)(background light)) + (:foreground "DimGray" ,@font)) + (((class grayscale)(background dark)) + (:foreground "LightGray" ,@font)) + (((class color)(background light)) + (:foreground "red" ,@font)) + (((class color)(background dark)) + (:foreground "red" ,@font)) + (t (,@font)))) + "Face for important keywords." + :group 'font-latex-highlighting-faces) + +(defface font-latex-verbatim-face + (let ((font '(:inherit fixed-pitch))) + `((((class grayscale) (background light)) + (:foreground "DimGray" ,@font)) + (((class grayscale) (background dark)) + (:foreground "LightGray" ,@font)) + (((class color) (background light)) + (:foreground "SaddleBrown" ,@font)) + (((class color) (background dark)) + (:foreground "burlywood" ,@font)) + (t (,@font)))) + "Face used to highlight TeX verbatim environments." + :group 'font-latex-highlighting-faces) + +(defface font-latex-superscript-face + '((t (:height 0.85))) + "Face used for superscripts." + :group 'font-latex-highlighting-faces) + +(defface font-latex-subscript-face + '((t (:height 0.85))) + "Face used for subscripts." + :group 'font-latex-highlighting-faces) + +(defface font-latex-script-char-face + (let ((font '(:inherit underline))) + `((((class grayscale) (background light)) + (:foreground "DarkGray" ,@font)) + (((class grayscale) (background dark)) + (:foreground "gray" ,@font)) + (((class color) (background light)) + (:foreground "salmon")) + (((class color) (background dark)) + (:foreground "DarkRed")) + (t (,@font)))) + "Face used for the script chars ^ and _." + :group 'font-latex-highlighting-faces) + +(defface font-latex-slide-title-face + '((t (:inherit (variable-pitch font-lock-type-face) + :weight bold :height 1.2))) + "Face for slide titles." + :group 'font-latex-highlighting-faces) + +;;; Setup + +(defvar font-latex-syntax-alist + ;; Use word syntax for @ because we use \> for matching macros and + ;; we don't want \foo@bar to be found if we search for \foo. + '((?\( . ".") (?\) . ".") (?@ . "w")) + "List of specifiers for the syntax alist of `font-lock-defaults'.") + +(defun font-latex-add-to-syntax-alist (list) + "Activate syntactic font locking for the entries in LIST. +The entries are added to `font-latex-syntax-alist' and eventually +end up in `font-lock-defaults'. Each entry in LIST should be a +cons pair as expected by `font-lock-defaults'. The function also +triggers Font Lock to recognize the change." + (set (make-local-variable 'font-latex-syntax-alist) + (append font-latex-syntax-alist list)) + ;; We modify the `font-lock-syntax-table' directly but also call + ;; `font-latex-setup' in order to have `font-lock-defaults' be in sync. + (font-latex-setup) + (dolist (elt list) + (modify-syntax-entry (car elt) (cdr elt) font-lock-syntax-table)) + ;; Trigger refontification. + (when (fboundp 'font-lock-flush) + (font-lock-flush))) + +(defun font-latex--make-syntax-propertize-function () + "Return a `syntax-propertize-function' for (La|Doc)TeX documents." + (let ((kws ;; (if (derived-mode-p 'doctex-mode) + ;; font-latex-doctex-syntactic-keywords + font-latex-syntactic-keywords)) ;; ) + (syntax-propertize-via-font-lock kws))) + +;;;###autoload +(defun font-latex-setup () + "Setup this buffer for LaTeX font-lock. Usually called from a hook." + (font-latex-set-syntactic-keywords) + + ;; Activate multi-line fontification facilities. + (set (make-local-variable 'font-lock-multiline) t) + + ;; The test for `major-mode' currently only works with docTeX mode + ;; because `TeX-install-font-lock' is called explicitly in + ;; `doctex-mode'. In case other modes have to be distinguished as + ;; well, remove the call to `TeX-install-font-lock' from + ;; `VirTeX-common-initialization' and place it in the different + ;; `xxx-mode' calls instead, but _after_ `major-mode' is set. + (let ((defaults + `((font-latex-keywords font-latex-keywords-1 font-latex-keywords-2) + nil nil ,font-latex-syntax-alist nil)) + (variables + '((font-lock-mark-block-function . mark-paragraph) + (font-lock-fontify-region-function + . font-latex-fontify-region) + (font-lock-unfontify-region-function + . font-latex-unfontify-region) + (font-lock-extend-region-functions + font-lock-extend-region-wholelines + font-lock-extend-region-multiline + font-latex-extend-region-backwards-command-with-args + font-latex-extend-region-backwards-command-in-braces + font-latex-extend-region-backwards-quotation + font-latex-extend-region-backwards-math) + (syntax-propertize-extend-region-functions + syntax-propertize-wholelines + font-latex-sp-extend-region-backwards-verb-env)))) + ;; Add the mode-dependent stuff to the basic variables defined above. + (if (eq major-mode 'doctex-mode) + (progn + (setcar defaults (append (car defaults) + '(font-latex-doctex-keywords))) + (setq variables + (append variables + '((font-lock-syntactic-face-function + . font-latex-doctex-syntactic-face-function))))) + (setq variables + (append variables + '((font-lock-syntactic-face-function + . font-latex-syntactic-face-function))))) + ;; Set the defaults. + (setq font-lock-defaults (append defaults variables))) + + ;; Make sure fontification will be refreshed if a user sets variables + ;; influencing fontification in her file-local variables section. + (add-hook 'hack-local-variables-hook #'font-latex-after-hacking-local-variables t t)) + +(defun font-latex-update-font-lock (&optional _syntactic-kws) + "Tell font-lock about updates of fontification rules. +If SYNTACTIC-KWS is non-nil, also update +`font-latex-syntactic-keywords'." + (display-warning + 'auctex + (concat "`font-latex-update-font-lock' should not be called. +It is obsolete and going to be removed. +If you have called `font-latex-add-keywords' and want to refresh fontification, +call `font-lock-flush' instead. +If you changed syntactic fontification, for example, one of the variables +- `LaTeX-verbatim-macros-with-delims' +- `LaTeX-verbatim-macros-with-delims-local' +- `LaTeX-verbatim-macros-with-braces' +- `LaTeX-verbatim-macros-with-braces-local' +- `LaTeX-verbatim-environments' +- `LaTeX-verbatim-environments-local' +- `font-latex-syntactic-keywords-extra' +then call `font-latex-set-syntactic-keywords'."))) + +(make-obsolete 'font-latex-update-font-lock nil "12.2.4") + +(defvar font-latex--updated-region-end nil +;; During font lock operation, matched range sometimes exceeds the +;; given end limit. So record the actual end in this variable to +;; notify the font lock machinery. +;; Match functions should do the following two if the end of the +;; actual match goes beyond the limit: +;; 1. If the value of this variable is smaller than limit, set this +;; variable to that limit. +;; 2. When the end of the actual match exceeds this variable, +;; - apply `font-lock-unfontify-region' between the value of this +;; variable and the end of the actual match +;; - update this variable to the end of the actual match +;; See implementation of `font-latex-match-math-env' for actual usage. + "Record the end of fontification.") +(defun font-latex-fontify-region (beg end &optional verbose) + "Fontify region from BEG to END. +Take care when the actually fonfified region was extended beyond END." + (setq font-latex--updated-region-end end) + (let ((res (font-lock-default-fontify-region beg end verbose))) + ;; COMPATIBILITY for older emacsen. Return value for jit-lock + ;; is meaningful for only newer emacsen. + (if (eq (car-safe res) 'jit-lock-bounds) + `(jit-lock-bounds ,(cadr res) . + ,(max (cddr res) font-latex--updated-region-end))))) + +;; Copy and adaption of `tex-font-lock-unfontify-region' from +;; tex-mode.el in GNU Emacs on 2004-08-04. +;; (XEmacs passes a third argument to the function.) +(defun font-latex-unfontify-region (beg end &rest _ignored) + "Unfontify region from BEG to END." + (font-lock-default-unfontify-region beg end) + (remove-list-of-text-properties beg end '(script-level invisible)) + (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) + (null (cddr prop))) + (put-text-property beg next 'display nil)) + (setq beg next)))) + +(defun font-latex-after-hacking-local-variables () + "Refresh fontification if required by updates of file-local variables. +This function is added to `hack-local-variables-hook' and +recomputes fontification if variables affecting fontification are +modified. Such variables include +`LaTeX-verbatim-environments-local', +`LaTeX-verbatim-macros-with-braces-local', +`LaTeX-verbatim-macros-with-delims-local'." + (when + ;; In Emacs we know if the value came from file or directory + ;; locals. Note to self: directory-local variables are also added + ;; to file-local-variables-alist. + (let ((hacked-local-vars (mapcar #'car file-local-variables-alist))) + (or (memq 'LaTeX-verbatim-environments-local hacked-local-vars) + (memq 'LaTeX-verbatim-macros-with-braces-local hacked-local-vars) + (memq 'LaTeX-verbatim-macros-with-delims-local hacked-local-vars))) + ;; Ok, we need to refresh syntactic fontification. + (font-latex-set-syntactic-keywords))) + +;;; Utility functions + +(defun font-latex-find-matching-close (openchar closechar) + "Skip over matching pairs of OPENCHAR and CLOSECHAR. +OPENCHAR is the opening character and CLOSECHAR is the closing +character. Character pairs are usually { } or [ ]. Comments are +ignored during the search." + (let ((parse-sexp-ignore-comments + (not (eq major-mode 'doctex-mode))) ; scan-sexps ignores comments + (init-point (point)) + (mycount 1) + (esc-char (or (and (boundp 'TeX-esc) TeX-esc) "\\")) + ;; XXX: Do not look up syntax-table properties since they may + ;; be misleading, e.g. in the case of "{foo}^^A" where the + ;; closing brace gets a comment end syntax. + (parse-sexp-lookup-properties nil)) + (or + (condition-case nil + (progn + (goto-char (with-syntax-table + (TeX-search-syntax-table openchar closechar) + (scan-sexps (point) 1))) + ;; No error code. See if closechar is unquoted + (save-excursion + (backward-char 1) + (zerop (mod (skip-chars-backward (regexp-quote esc-char)) 2)))) + (error nil)) + (save-match-data + (goto-char (1+ init-point)) + (while (and (> mycount 0) + (re-search-forward + (string ?\[ + ;; closechar might be ] + ;; and therefor must be first in regexp + closechar openchar + ?\]) + nil t)) + (cond + ((font-latex-commented-outp) + (forward-line 1)) + ((save-excursion + (backward-char 1) + (zerop (mod (skip-chars-backward (regexp-quote esc-char)) + 2))) + (setq mycount (+ mycount + (if (= (preceding-char) openchar) 1 -1))))))) + (if (= mycount 0) + t + (goto-char init-point) + nil)))) + +(defun font-latex-commented-outp () + "Return t if comment character is found between bol and point." + (save-excursion + (let ((limit (point)) + (esc-char (if (and (boundp 'TeX-esc) TeX-esc) TeX-esc "\\"))) + (forward-line 0) + (if (and (eq (char-after) ?\%) + (not (font-latex-faces-present-p 'font-latex-verbatim-face))) + (not (eq major-mode 'doctex-mode)) + (catch 'found + (while (progn (skip-chars-forward "^%" limit) + (< (point) limit)) + (when (and (save-excursion + (zerop (mod (skip-chars-backward + (regexp-quote esc-char)) 2))) + (not (font-latex-faces-present-p + 'font-latex-verbatim-face))) + (throw 'found t)) + (forward-char))))))) + +(defun font-latex-faces-present-p (faces &optional pos) + "Return t if FACES are present at position POS. +FACES may be a single face or a list of faces. +If POS is omitted, the current position of point is used." + (let* ((faces (if (listp faces) faces (list faces))) + (pos (or pos (point))) + (prop (get-text-property pos 'face)) + (prop-list (if (listp prop) prop (list prop)))) + (catch 'member + (dolist (item prop-list) + (when (memq item faces) + (throw 'member t)))))) + +(defun font-latex-forward-comment () + "Like `forward-comment' but with special provisions for docTeX mode. +In docTeX mode \"%\" at the start of a line will be treated as whitespace." + (if (eq major-mode 'doctex-mode) + ;; XXX: We should probably cater for ^^A as well. + (progn + (while (progn (if (bolp) (skip-chars-forward "%")) + (> (skip-chars-forward " \t\n") 0))) + (when (eq (char-after) ?%) + (beginning-of-line 2) + t)) + (forward-comment 1))) + +;;; Match functions + +(defvar font-latex-matched-faces nil + "List of faces corresponding to matches in match data.") + +(defun font-latex-matched-face (pos) + "Return face at position POS in `font-latex-matched-faces'." + (nth pos font-latex-matched-faces)) + +(defvar font-latex-command-with-args-default-spec nil ; "*[{" + "Default specifier for keywords without syntax description. +Set this to nil if verification of command syntax is unwanted.") + +(defvar font-latex-command-with-args-opt-arg-delims + '((?\[ . ?\]) (?< . ?>) (?\( . ?\))) + "List character pairs used as delimiters for optional arguments.") + +(defvar font-latex-syntax-error-modes '(latex-mode) + "List of modes where syntax errors in macros should be indicated.") + +(defun font-latex-match-command-with-arguments (regexp keywords face limit) + "Search for regexp command KEYWORDS[opt]{arg} before LIMIT. +Returns nil if none of KEYWORDS is found." + (setq font-latex-matched-faces nil) + (catch 'match + (while (re-search-forward regexp limit t) + (unless (font-latex-faces-present-p '(font-lock-comment-face + font-latex-verbatim-face) + (match-beginning 0)) + (let* ((beg (match-beginning 0)) + end ; Used for multiline text property. + (match-data (list beg)) + match-beg syntax-error alternative spec + error-indicator-pos + (spec-list (string-to-list + (or (cadr (assoc (match-string 1) keywords)) + font-latex-command-with-args-default-spec))) + (parse-sexp-ignore-comments t)) ; scan-sexps ignores comments + (goto-char (match-end 0)) + ;; Check for starred macro if first spec is an asterisk or a + ;; plus sign in case of \defaultfontfeatures+ provided by + ;; fontspec.sty + (when (or (eq (car spec-list) ?*) + (eq (car spec-list) ?+)) + (setq spec-list (cdr spec-list)) + (skip-chars-forward "*+" (1+ (point)))) + ;; Add current point to match data and use keyword face for + ;; region from start to point. + (nconc match-data (list (point))) + (add-to-list 'font-latex-matched-faces 'font-lock-keyword-face) + (setq end (point)) + (catch 'break + ;; Walk the list of specs. + (while spec-list + (setq spec (pop spec-list) + error-indicator-pos beg) + (while (and (not (eobp)) (font-latex-forward-comment))) + ;; Alternative + (when (eq spec ?|) + (setq alternative t) + (setq spec (pop spec-list))) + (cond + ;; Macros: \foo + ((eq spec ?\\) + (if (eq (char-after) spec) + (progn + (nconc match-data + (list (point) + (progn + (forward-char) + (if (zerop (skip-syntax-forward "_w")) + (forward-char) ; Single-char macro. + (skip-chars-forward "*+")) + (point)))) + (nconc font-latex-matched-faces (list face)) + (setq end (max end (point))) + (when alternative (pop spec-list))) + (setq syntax-error t) + (throw 'break nil))) + ;; Mandatory arguments: {...} + ((eq spec ?{) + (if (and (eq (char-after) spec) + (setq match-beg (point)) + (font-latex-find-matching-close ?{ ?})) + (progn + (nconc match-data (list (1+ match-beg) (1- (point)))) + (nconc font-latex-matched-faces (list face)) + (setq end (max end (1- (point)))) + (when alternative (pop spec-list))) + (unless alternative + (setq syntax-error t) + (when (and match-beg (= match-beg (point))) + (setq error-indicator-pos match-beg)) + (throw 'break nil)))) + ;; Optional arguments: [...] and others + ((eq (char-after) spec) + (setq match-beg (point)) + (if (font-latex-find-matching-close + spec (cdr (assq + spec + font-latex-command-with-args-opt-arg-delims))) + (progn + (nconc match-data (list (1+ match-beg) (1- (point)))) + (nconc font-latex-matched-faces + (list 'font-lock-variable-name-face)) + (setq end (max end (1- (point))))) + (setq syntax-error t + error-indicator-pos match-beg) + (throw 'break nil)))) + (setq alternative nil))) + (when (and syntax-error (memq major-mode + font-latex-syntax-error-modes)) + ;; Add the warning face at the front of the list because + ;; the matcher uses 'append and the face would otherwise + ;; be overridden by the keyword face. + (setq match-data (append (list error-indicator-pos + (1+ error-indicator-pos)) + match-data)) + (push 'font-latex-warning-face font-latex-matched-faces)) + (store-match-data match-data) + (throw 'match t)))))) + +;; Those are dynamically bound by font-lock. +(defvar font-lock-beg) +(defvar font-lock-end) + +(defun font-latex-extend-region-backwards-command-with-args () + "Extend region backwards for commands with args." + (save-excursion + (goto-char font-lock-end) + (catch 'extend + (while (TeX-search-backward-unescaped "}" font-lock-beg t) + (let ((macro-start + (TeX-find-macro-start + (max (point-min) + (- font-lock-beg font-latex-multiline-boundary))))) + (when (and macro-start + (< macro-start font-lock-beg)) + (setq font-lock-beg macro-start) + (throw 'extend t))))))) + +(defun font-latex-match-command-in-braces (keywords limit) + "Search for command like {\\bfseries fubar} before LIMIT. +Sets `match-data' so that: + subexpression 0 is a warning indicator, + subexpression 1 is the keyword, and + subexpression 2 is the rest in the TeX group. +Return nil if no command is found." + (catch 'match + (while (re-search-forward keywords limit t) + (unless (font-latex-faces-present-p '(font-lock-comment-face + font-latex-verbatim-face) + (match-beginning 0)) + (let ((kbeg (match-beginning 0)) (kend (match-end 1)) + (beg (match-end 0)) + cbeg cend + (parse-sexp-ignore-comments t)) ; scan-sexps ignores comments + (goto-char kbeg) + (if (not (eq (preceding-char) ?\{)) + ;; Fontify only the keyword (no argument found). + (progn + (setq cbeg kbeg cend kend) + (goto-char (match-end 0)) + (store-match-data (list (point) (point) + (point) (point) + cbeg cend)) + (throw 'match t)) + ;; There's an opening bracket + (save-restriction + ;; Restrict to LIMIT. + (narrow-to-region (point-min) limit) + (forward-char -1) ; Move on the opening bracket + (if (font-latex-find-matching-close ?\{ ?\}) + (store-match-data (list kbeg kbeg + kbeg kend + beg (1- (point)))) + (goto-char kend) + (store-match-data (list (1- kbeg) kbeg + kbeg kend + kend kend))) + (throw 'match t)))))))) + +(defun font-latex-extend-region-backwards-command-in-braces () + "Extend region backwards for commands in braces." + (save-excursion + (goto-char font-lock-end) + (catch 'extend + (while (TeX-search-backward-unescaped "}" font-lock-beg t) + (let ((group-start + (TeX-find-opening-brace + nil (max (point-min) + (- font-lock-beg font-latex-multiline-boundary))))) + (when group-start + ;; XXX: Actually we'd have to check if any of the + ;; declaration-type macros can be found right after the + ;; brace. If we don't do this (like now) large regions + ;; may be refontified for no good reason. For checking + ;; the built-in `font-latex-match-*' variables for + ;; declaration-type macros as well as the respective + ;; user-defined variables could be concatenated. + (goto-char group-start) + (when (< group-start font-lock-beg) + (setq font-lock-beg group-start) + (throw 'extend t)))))))) + +(defvar font-latex-match-simple-exclude-list + '("-" "," "/" "&" "#" "_" "`" "'" "^" "~" "=" "." "\"") + "List of characters directly after \"\\\" excluded from fontification. +Each character is a string.") + +(defvar font-latex-match-simple-include-list '("@") + "List of characters allowed in a macro for fontification. +Each character is a string. This variable is initialized to +\"@\" since internal LaTeX commands are very often redefined in a +.tex file and the fontification should work correctly in those +cases.") +(make-variable-buffer-local 'font-latex-match-simple-include-list) + +(defun font-latex-match-simple-command (limit) + "Search for command like \\foo before LIMIT." + ;; \s_ matches chars with symbol syntax, \sw chars with word syntax, + ;; \s. chars with punctuation syntax. We must exclude matches where + ;; the first character after the \ is a reserved character and + ;; should not be fontified (e.g. \, in foo\,bar or \- in foo\-bar). + ;; These characters are stored in + ;; `font-latex-match-simple-exclude-list'. In docTeX mode, we + ;; remove "_" from this list to get correct fontification for macros + ;; like `\__module_foo:nnn' + (let* ((search (lambda () + (TeX-re-search-forward-unescaped + (concat + ;; Chars directly after backslash + "\\\\\\(\\s_\\|\\sw\\|\\s.\\)" + ;; Start group of the following chars + "\\(?:[" + ;; a-zA-Z are always allowed: + "a-zA-Z" + ;; Additional characters added by AUCTeX styles + (mapconcat #'identity + font-latex-match-simple-include-list + "") + ;; End group + "]\\)*") + limit t))) + (pos (funcall search))) + (while (and pos + (member (match-string 1) + (if (eq major-mode 'doctex-mode) + (remove "_" font-latex-match-simple-exclude-list) + font-latex-match-simple-exclude-list))) + (setq pos (funcall search))) + pos)) + +(defun font-latex-match-math-env (limit) + "Match math pattern up to LIMIT. +Used for patterns like: +\\( F = ma \\) +\\=\\[ F = ma \\] but not \\\\=\\[len]" + (catch 'match + (while (re-search-forward "\\(\\\\(\\)\\|\\(\\\\\\[\\)" limit t) + (unless (save-excursion + (goto-char (match-beginning 0)) + ;; \\[ does not start a math environment + (/= (mod (skip-chars-backward "\\\\") 2) 0)) + (let ((beg (match-beginning 0)) + (open-tag (if (match-beginning 1) "\\(" "\\[")) + (close-tag (if (match-beginning 1) "\\)" "\\]"))) + ;; Search for both opening and closing tags in order to be + ;; able to avoid erroneously matching stuff like "\(foo \(bar\)". + (if (and (re-search-forward (concat "[^\\]\\(?:\\\\\\\\\\)*\\(" + (regexp-quote open-tag) "\\|" + (regexp-quote close-tag) "\\)") + (+ limit font-latex-multiline-boundary) + 'move) + (string= (match-string 1) close-tag)) + ;; Found closing tag. + (let ((p (point))) + (if (< font-latex--updated-region-end limit) + ;; *-extend-region-functions have extended the + ;; limit already. + (setq font-latex--updated-region-end limit)) + ;; If the closing tag is beyond the current end of + ;; region, take care of it. + (when (< font-latex--updated-region-end p) + (font-lock-unfontify-region font-latex--updated-region-end p) + (setq font-latex--updated-region-end p)) + (store-match-data (list beg beg beg p))) + ;; Did not find closing tag. + (goto-char (+ beg 2)) + (store-match-data (list beg (point) (point) (point)))) + (throw 'match t)))))) + +(require 'texmathp) +(defcustom font-latex-math-environments nil + "List of math environment names for font locking. +It is no longer recommended to customize this option. You should +customize `texmathp-tex-commands' instead because it is important +for stable operation of font lock that this option is coherent +with that option in addition to `texmathp-tex-commands-default'. +See info node `(auctex)Fontification of math' to convert your +customization into `texmathp-tex-commands'." + ;; This option is now used only through + ;; `font-latex--match-math-envII-regexp'. + :type '(repeat string) + :group 'font-latex) + +(defvar font-latex--match-math-envII-regexp nil + "Regular expression to match math environments. +Set by `font-latex--update-math-env' and used in +`font-latex-match-math-envII'.") + +(defun font-latex-update-math-env () + "Update regexp to search for math environments. +Extract environments marked as `env-on' in +`texmathp-tex-commands1' except starred variants. Then build +`font-latex--match-math-envII-regexp' from them, appending the +environments in `font-latex-math-environments'." + ;; Make sure `texmathp-tex-commands1' is up to date. + (texmathp-compile) + (let (envs) + (dolist (entry texmathp-tex-commands1) + (if (and (eq 'env-on (cadr entry)) + (not (string= "*" (substring (car entry) -1)))) + (cl-pushnew (car entry) envs :test #'equal))) + (setq font-latex--match-math-envII-regexp + (concat "\\\\begin[ \t]*{" + ;; Take user additions also into account. + (regexp-opt (append font-latex-math-environments envs) t) + ;; Subexpression 2 is used to build the \end{} + ;; construct later. + "\\(\\*?}\\)" + ;; Match an optional and possible mandatory + ;; argument(s) as long as they are on the same line + ;; with no spaces in-between. The content of optional + ;; argument can span multiple lines. + "\\(?:\\[[^][]*\\(?:\\[[^][]*\\][^][]*\\)*\\]\\)?" + "\\(?:{[^}]*}\\)*")))) + +;; Initialize. +(font-latex-update-math-env) + +(defun font-latex-match-math-envII (limit) + "Match math patterns up to LIMIT. +Used for patterns like: +\\begin{equation} + fontified stuff +\\end{equation} or +\\begin{empheq}[X=Y\\Rightarrow]{alignat=3} + fontified stuff +\\end{empheq} +The \\begin{equation} incl. arguments in the same line and +\\end{equation} are not fontified here." + (when (re-search-forward font-latex--match-math-envII-regexp limit t) + (let ((beg (match-end 0)) end + (beg-of-begin (match-beginning 0))) + (if (re-search-forward (concat "\\\\end[ \t]*{" + (regexp-quote + (buffer-substring-no-properties + (match-beginning 1) + (match-end 2)))) + (+ limit font-latex-multiline-boundary) 'move) + (progn + (setq end (match-beginning 0)) + (if (< font-latex--updated-region-end limit) + (setq font-latex--updated-region-end limit)) + (when (< font-latex--updated-region-end end) + (font-lock-unfontify-region font-latex--updated-region-end end) + (setq font-latex--updated-region-end end))) + (goto-char beg) + (setq end beg + beg-of-begin beg)) + ;; Store the position of "\begin{foo}" as (match-beginnig 0) so + ;; that `font-lock-multiline' text property covers it. This keeps + ;; editing inside multi-line optional argument sane. + (store-match-data (list beg-of-begin end beg end)) + t))) + +(defun font-latex-match-dollar-math (limit) + "Match inline math $...$ or display math $$...$$ before LIMIT." + (catch 'match + (let (beg num) + (while (font-latex-find-dollar-math limit) + ;; Found "$" which starts $...$ or $$...$$. + (setq beg (point) + ;; Go inside the math expression. + num (skip-chars-forward "$" limit)) + ;; If those are three or more consecutive $, ignore them and + ;; search again. + (if (< num 3) + (if ;; Let's find the same number of live dollar signs. + (font-latex-find-dollar-math + ;; Hope that limit+font-latex-multiline-boundary + ;; doesn't fall just inside single "$$". + (min (point-max) (+ limit font-latex-multiline-boundary)) + num) + ;; Found. + (progn + (forward-char num) + (let ((p (point))) + (if (< font-latex--updated-region-end limit) + (setq font-latex--updated-region-end limit)) + (when (< font-latex--updated-region-end p) + (font-lock-unfontify-region + font-latex--updated-region-end p) + (setq font-latex--updated-region-end p)) + (set-match-data (list beg p))) + (throw 'match t)) + ;; Not found. + ;; That $ or $$ is probably unclosed in the buffer. + (throw 'match nil))))))) + +(defun font-latex-find-dollar-math (limit &optional num) + "Find dollar sign(s) before LIMIT. +Set point just before the found $. Ignore escaped $ (\"\\$\"). +Optional argument NUM, if non-nil, specifies the number of dollar +signs to follow the point and must be 1 or 2. +LIMIT must not exceed the end of buffer." + (catch 'found + (while (progn + (skip-chars-forward "^$" limit) + (< (point) limit)) + ;; Found "$". + ;; If that "$" is not our target, skip over it and search + ;; again. + (cond + ;; check 1: Are we in a verbatim construct or comment? + ((let ((ppss (syntax-ppss))) + (or (nth 3 ppss) + ;; Ignore $ in comments... + (and (nth 4 ppss) + ;; ... except if we're looking for the end of the + ;; inline math. We need to consider this %$ + ;; comments because they are the workaround for + ;; falsely triggered math mode due to valid, + ;; non-math occurrences of $. (bug#48365) + (not num)))) + (skip-chars-forward "$" limit)) + ;; check 2: Else, is "$" escaped? + ((TeX-escaped-p) + (forward-char 1)) + ;; check 3: Else, is the number of the following "$" wrong? + ;; This check cannot precede check 2 because "$1+2\$$" is + ;; legal. + ((and (eq num 2) (not (eq (char-after (1+ (point))) ?$))) + ;; If double dollars ($$) are followed by $, skip over that $. + ;; We need not care the case that single dollar ($) is + ;; followed by $$ because expressions like "$1+1$$2+2$" and + ;; "$1+2$$$3+3$$" are legal. + (forward-char 1)) + (t + ;; That "$" is live one. + (throw 'found t)))))) + +(defun font-latex-extend-region-backwards-math () + "Extend region backwards for math environmets. +Take into account $...$, $$...$$, \\(...\\) and \\=\\[...\\], too." + ;; Use `texmathp' to identify whether the point is inside math mode. + ;; Only heuristic, but it's very difficult to identify rigorously + ;; without syntactic support. + + ;; Check if `font-lock-beg' is inside math mode. + (goto-char font-lock-beg) + + ;; Workaround bug#41522. Ensure `syntax-table' property is given to + ;; all verbatim-like constructs up to the position before running + ;; `texmathp' in order to prevent wrong fontification of verbatim + ;; face. This is necessary because `texmathp' calls `up-list' inside + ;; narrowing. + (syntax-propertize (point)) + + ;; XXX: Should we make the `texmathp' search honor + ;; `font-latex-multiline-boundary'? + (when (and (texmathp) (< (cdr texmathp-why) font-lock-beg)) + ;; Make its beginning a new start of font lock region. + (setq font-lock-beg (cdr texmathp-why)) + t)) + +(defun font-latex-sp-extend-region-backwards-verb-env (beg end) + "Extend region backwards for verbatim environments." + (let ((envs (and (fboundp 'LaTeX-verbatim-environments) + (LaTeX-verbatim-environments)))) + (when envs + (save-excursion + (goto-char end) + (catch 'extend + (while (re-search-backward + (concat "\\\\end[ \t]*{" (regexp-opt envs t) "\\*?}") beg t) + (when (and (re-search-backward + (concat "\\\\begin[ \t]*{" + (buffer-substring-no-properties + (match-beginning 1) + (match-end 0)) + ;; Match an optional and possible + ;; mandatory argument(s) + "\\(?:\\[[^][]*\\(?:\\[[^][]*\\][^][]*\\)*\\]\\)?" + "\\(?:{[^}]*}\\)*") + (- beg font-latex-multiline-boundary) t) + (< (point) beg)) + (throw 'extend (cons (point) end))))))))) + +(defun font-latex-update-quote-list () + "Update quote list and regexp if value of `font-latex-quotes' changed." + (unless (eq font-latex-quotes-control font-latex-quotes) + (setq font-latex-quotes-control font-latex-quotes) + (font-latex-quotes-set-internal) + ;; Set order of each entry in `font-latex-quote-list' according to + ;; setting of `font-latex-quotes-internal'. + (let ((tail font-latex-quote-list) + elt) + (while tail + (setq elt (car tail)) + (when (and (> (safe-length elt) 2) + (not (eq (nth 2 elt) font-latex-quotes-internal))) + (setcar tail (list (nth 1 elt) (nth 0 elt) + font-latex-quotes-internal))) + (setq tail (cdr tail)))) + (setq font-latex-quote-regexp-beg + (regexp-opt (mapcar #'car font-latex-quote-list) t)))) + +(defun font-latex-match-quotation (limit) + "Match quote patterns up to LIMIT. +Used for patterns like: +``this is a normal quote'' and these are multilingual quoted strings: +\"< french \"> and \"`german\"' quotes. +The quotes << french >> and 8-bit french are used if `font-latex-quotes' is +set to `french', and >>german<< (and 8-bit) are used if set to `german'." + (when font-latex-quotes + (font-latex-update-quote-list) + ;; Search for matches. + (catch 'match + (while (TeX-re-search-forward-unescaped + font-latex-quote-regexp-beg limit t) + (unless (font-latex-faces-present-p '(font-lock-comment-face + font-latex-verbatim-face + font-latex-math-face) + (match-beginning 0)) + (let* ((beg (match-beginning 0)) + (after-beg (match-end 0)) + (opening-quote (match-string-no-properties 0)) + (closing-quote + (nth 1 (assoc opening-quote font-latex-quote-list))) + (nest-count 0) + (point-of-surrender (+ beg font-latex-multiline-boundary))) + ;; Find closing quote taking nested quotes into account. + (while (progn + (re-search-forward + (concat opening-quote "\\|" closing-quote) + point-of-surrender 'move) + (when (and (< (point) point-of-surrender) (not (eobp))) + (if (string= (match-string-no-properties 0) + opening-quote) + (setq nest-count (1+ nest-count)) + (when (/= nest-count 0) + (setq nest-count (1- nest-count))))))) + ;; If no closing quote was found, set the second match which + ;; will be marked with warning color, if one was found, set + ;; the first match which will be marked with string color. + (if (or (= (point) point-of-surrender) (eobp)) + (progn + (goto-char after-beg) + (store-match-data (list after-beg after-beg beg after-beg))) + (let ((p (point))) + (if (< font-latex--updated-region-end limit) + (setq font-latex--updated-region-end limit)) + (when (< font-latex--updated-region-end p) + (font-lock-unfontify-region + font-latex--updated-region-end p) + (setq font-latex--updated-region-end p)) + (store-match-data (list beg p p p)))) + (throw 'match t))))))) + +(defun font-latex-extend-region-backwards-quotation () + "Extend region backwards for quotations." + (when font-latex-quotes + (font-latex-update-quote-list) + (let ((regexp-end (regexp-opt (mapcar #'cadr font-latex-quote-list) t))) + (save-excursion + (goto-char font-lock-end) + (catch 'extend + (while (re-search-backward regexp-end font-lock-beg t) + (let ((found-end (match-beginning 0)) + (closing-quote (match-string-no-properties 0)) + (nest-count 0) + (point-of-surrender (- font-lock-beg + font-latex-multiline-boundary)) + opening-quote) + (catch 'found + (dolist (elt font-latex-quote-list) + (when (string= (cadr elt) closing-quote) + (setq opening-quote (car elt)) + (throw 'found nil)))) + ;; Find opening quote taking nested quotes into account. + (while (if (re-search-backward (concat opening-quote "\\|" + closing-quote) + point-of-surrender 'move) + ;; Found quotes before point-of-surrender. + (cond ((string= (match-string-no-properties 0) + closing-quote) + ;; Encountered another closing quote. + ;; Increase nest-count and continue + ;; the inner loop. + (setq nest-count (1+ nest-count))) + ;; Found an opening quote. + ((/= nest-count 0) + ;; If in nest, decrease nest-count + ;; and continue the inner loop. + (setq nest-count (1- nest-count))) + ;; Else we arrived at the opening quote + ;; matching with the closing quote found + ;; in the outer loop. + ((< (point) font-lock-beg) + ;; If that opening quote locates + ;; before `font-lock-beg', break the + ;; outer loop and extend the region. + (setq font-lock-beg (point)) + (throw 'extend t)) + (t + ;; Else terminate the inner loop and + ;; continue the outer loop. + nil)) + ;; Didn't find quotes before + ;; point-of-surrender. + ;; Go back just before the closing quote, + ;; terminate the inner loop and + ;; continue the outer loop. + (goto-char found-end) + nil))))))))) + +(defun font-latex-match-script (limit) + "Match subscript and superscript patterns up to LIMIT." + (when (and font-latex-fontify-script + (re-search-forward "[_^] *\\([^\n\\{}]\\|\ +\\\\\\([a-zA-Z@]+\\|[^ \t\n]\\)\\|\\({\\)\\)" limit t)) + (if (and (not (memq font-latex-fontify-script '(multi-level invisible))) + (font-latex-faces-present-p '(font-latex-subscript-face + font-latex-superscript-face))) + ;; Apply subscript and superscript highlighting only once (in case + ;; font-latex-fontify-script is not 'multi-level) in order to prevent + ;; the font size becoming too small. We set an empty match to do that. + (let ((point (point))) + (store-match-data (list point point point point))) + (when (match-end 3) + (let ((beg (match-beginning 3)) + (end (TeX-find-closing-brace + ;; Don't match groups spanning more than one line + ;; in order to avoid visually wrong indentation in + ;; subsequent lines. + nil (line-end-position)))) + (store-match-data (if end + (list (match-beginning 0) end beg end) + (list beg beg beg beg)))))) + t)) + +(defun font-latex-match-script-chars (limit) + "Match subscript and superscript chars up to LIMIT." + (catch 'found + (while (re-search-forward "[^_^]\\([_^]\\)" limit t) + (let ((pos (match-beginning 1))) + (when (and (font-latex-faces-present-p 'font-latex-math-face pos) + (not (font-latex-faces-present-p '(font-lock-constant-face + font-lock-builtin-face + font-lock-comment-face + font-latex-verbatim-face) pos)) + ;; Check for backslash quoting + (not (let ((odd nil) + (pos pos)) + (while (eq (char-before pos) ?\\) + (setq pos (1- pos) odd (not odd))) + odd))) + (throw 'found t)))))) + +(defun font-latex--get-script-props (pos script-type) + (let* ((old-raise (or (plist-get (get-text-property pos 'display) 'raise) 0.0)) + (new-level (1+ (or (get-text-property pos 'script-level) 0))) + (disp-props (copy-sequence (cl-case script-type + (:super (cdr font-latex-script-display)) + (:sub (car font-latex-script-display))))) + (new-disp-props (let ((raise (plist-get disp-props 'raise)) + (nl new-level)) + (if raise + ;; This polynom approximates that the factor + ;; which is multiplied with raise is 1 for nl=1, + ;; 0.8 for nl=2, 0.64 for nl=3, etc. (so always + ;; about 80% of the previous value). + (plist-put disp-props 'raise + (+ old-raise + (* raise + (+ 1.1965254857142873 + (* nl -0.21841226666666758) + (* nl nl 0.012018514285714385))))) + disp-props)))) + `(face ,(if (<= new-level font-latex-fontify-script-max-level) + (cl-case script-type + (:super 'font-latex-superscript-face) + (:sub 'font-latex-subscript-face)) + nil) + script-level ,new-level + display ,new-disp-props))) + +;; Copy and adaption of `tex-font-lock-suscript' from tex-mode.el in +;; GNU Emacs on 2004-07-07. +(defun font-latex-script (pos) + "Return face and display spec for subscript and superscript content." + (when (and (font-latex-faces-present-p 'font-latex-math-face pos) + (not (font-latex-faces-present-p '(font-lock-constant-face + font-lock-builtin-face + font-lock-comment-face + font-latex-verbatim-face) pos)) + ;; Check for backslash quoting + (not (let ((odd nil) + (pos pos)) + (while (eq (char-before pos) ?\\) + (setq pos (1- pos) odd (not odd))) + odd))) + (if (eq (char-after pos) ?_) + (font-latex--get-script-props pos :sub) + (font-latex--get-script-props pos :super)))) + +(defun font-latex-script-char (_pos) + "Return face and display spec for subscript and superscript character at POS." + `(face font-latex-script-char-face + ,@(when (eq font-latex-fontify-script 'invisible) + '(invisible t)))) + +;;; docTeX + +(defvar font-latex-doctex-preprocessor-face + 'font-latex-doctex-preprocessor-face + "Face used to highlight preprocessor directives in docTeX mode.") + +(defface font-latex-doctex-preprocessor-face + '((t (:inherit (font-latex-doctex-documentation-face + font-lock-builtin-face ; Emacs 21 does not provide + ; the preprocessor face. + font-lock-preprocessor-face)))) + "Face used to highlight preprocessor directives in docTeX mode." + :group 'font-latex-highlighting-faces) + +(defvar font-latex-doctex-documentation-face + 'font-latex-doctex-documentation-face + "Face used to highlight the documentation in docTeX mode.") + +(defface font-latex-doctex-documentation-face + '((((class mono)) (:inverse-video t)) + (((class grayscale) (background dark)) (:background "#333")) + (((class color) (background dark)) (:background "#333")) + (t (:background "#eeeeee"))) + "Face used to highlight the documentation parts in docTeX mode." + :group 'font-latex-highlighting-faces) + +(defvar font-latex-doctex-keywords + (append '((font-latex-doctex-match-^^A 0 font-lock-comment-face t)) + font-latex-keywords-2 + '(("^%<[^>]*>" (0 font-latex-doctex-preprocessor-face t))))) + +(defun font-latex-doctex-match-^^A (limit) + "In docTeX mode, match comment started by ^^A and ^^X before LIMIT." + (catch 'found + (while (TeX-re-search-forward-unescaped "\\^\\^[AX]" limit t) + (when (eq (char-after (line-beginning-position)) ?\%) + (forward-line 1) + ;; Adjust `font-latex--updated-region-end' if necessary. + (let ((p (point))) + (if (< font-latex--updated-region-end limit) + (setq font-latex--updated-region-end limit)) + (when (< font-latex--updated-region-end p) + (font-lock-unfontify-region + font-latex--updated-region-end p) + (setq font-latex--updated-region-end p)) + ;; We assume that the above adjustment preserves match data. + (set-match-data (list (match-beginning 0) p))) + (throw 'found t))))) + +;; Copy and adaptation of `doctex-font-lock-syntactic-face-function' +;; in `tex-mode.el' of CVS Emacs (March 2004) +(defun font-latex-doctex-syntactic-face-function (state) + ;; Mark docTeX documentation, which is parsed as a style A comment + ;; starting in column 0. + (if (or (nth 3 state) (nth 7 state) + (not (memq (char-before (nth 8 state)) + '(?\n nil)))) + ;; Anything else is just as for LaTeX. + (font-latex-syntactic-face-function state) + font-latex-doctex-documentation-face)) + + +;;; Installation in non-AUCTeX LaTeX mode + +;; Here used to be some code which tried to magically make things work and +;; thereby broke other stuff. If you want to use font-latex with stock +;; latex-mode, then please just add `font-latex-setup' to `latex-mode-hook' +;; yourself. + +;;; Byte-compilation of generated functions +;; Not needed now that we generate the code via a macro. +;; (when (byte-code-function-p +;; (symbol-function 'font-latex-make-built-in-keywords)) +;; (dolist (elt font-latex-built-in-keyword-classes) +;; (let ((name (nth 0 elt))) +;; (byte-compile (intern (concat "font-latex-match-" name))) +;; (byte-compile (intern (concat "font-latex-match-" name "-make")))))) + + +;; Provide ourselves: +(provide 'font-latex) + +;; Local Variables: +;; coding: utf-8 +;; End: + +;;; font-latex.el ends here -- cgit v1.2.1