;;; math-preview.el --- Preview TeX math equations inline -*- lexical-binding: t -*-
;; Author: Matsievskiy S.V.
;; Maintainer: Matsievskiy S.V.
;; Version: 5.1.1
;; Package-Requires: ((emacs "26.1") (json "1.4") (dash "2.18.0") (s "1.12.0"))
;; Homepage: https://gitlab.com/matsievskiysv/math-preview
;; Keywords: convenience
;; This file is not part of GNU Emacs
;; This file 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 program 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.
;; For a full copy of the GNU General Public License
;; see .
;;; Commentary:
;; Preview TeX math equations inline using MathJax
;; This package requires external program math-preview.
;; Installation instructions are available in README.md file.
;;; Code:
(require 'json)
(require 'dash)
(require 's)
(require 'xml)
;; {{{ Customization
(defgroup math-preview nil
"Preview math inline."
:group 'text
:tag "Math Preview"
:prefix "math-preview-"
:link '(url-link :tag "GitLab" "https://gitlab.com/matsievskiysv/math-preview"))
(defface math-preview-face
'((t :inherit default))
"Face for equation.")
(defface math-preview-processing-face
'((t :inherit highlight))
"Face for equation processing.")
(defcustom math-preview-command "math-preview"
"TeX conversion program name."
:tag "Command name"
:type '(choice (string :tag "Command name")
(repeat :tag "Command arguments" (string :tag "Argument")))
:safe (lambda (n) (or (stringp n)
(and (listp n)
(-all-p #'stringp n)))))
(defcustom math-preview-raise 0.4
"Adjust vertical position of inline images.
This depends on `math-preview-raise-enable'."
:tag "Image vertical position"
:type 'number
:safe (lambda (n) (and (numberp n)
(> n 0)
(< n 1))))
(defcustom math-preview-raise-enable nil
"Enable vertical position adjustment for inline images.
When set to nil (the default), inline images are auto-positioned
according to their \\=`vertical-align\\=` properties. For any other
value, their position is controlled by `math-preview-raise'."
;; If the default of auto-positioning proves sufficient, then `math-preview-raise` and `-raise-enable`
;; might eventually be deprecated.
:tag "Image vertical position enable"
:type 'boolean
:safe t)
(defcustom math-preview-margin '(5 . 5)
"Adjust image margin."
:tag "Image margin"
:type '(cons :tag "Configure margins" (integer :tag "Horizontal") (integer :tag "Vertical"))
:safe (lambda (l) (and (consp l)
(integerp (car l))
(> (car l) 0)
(integerp (cdr l))
(> (cdr l) 0))))
(defcustom math-preview-relief 0
"Adjust image relief."
:tag "Image relief"
:type 'integer
:safe (lambda (n) (and (integerp n)
(> n 0))))
(defcustom math-preview-scale 1
"Adjust image scale."
:tag "Image scale"
:type 'number
:safe (lambda (n) (and (numberp n)
(> n 0))))
(defcustom math-preview-svg-postprocess-functions
'((lambda (x) (puthash 'string (s-replace "width=\"100%\""
(format "width=\"%dem\""
(/ (window-max-chars-per-line) 2))
(gethash 'string x))
x)))
"Functions to call on resulting SVG string before rendering.
Functions are applied in chain from left to right (or from top to bottom, when
in `customize'). Each function accepts one arguments which is a hash table
with field `string'. User may modify `string' in place to edit resulting image."
:tag "Postprocess SVG functions"
:type '(repeat function)
:safe (lambda (n) (and (listp n)
(-all? #'functionp n))))
(defcustom math-preview-scale-increment 0.1
"Image scale interactive increment value."
:tag "Image scale increment"
:type 'number
:safe (lambda (n) (and (numberp n)
(> n 0))))
(defcustom math-preview-preprocess-functions (list)
"Functions to call on each matched string.
Functions are applied in chain from left to right (or from top to bottom, when
in `customize'). Each function accepts one arguments which is a hash table
with fields: `match' matched string including marks; `string' matched string
without marks; `type' equation type (`tex', `mathml' or `asciimath');
`inline' equation inline flag; `lmark' and `rmark' are left and right marks
respectively. User may modify `string', `inline' and `type' fields in place to
influence further equation processing (although the intended purpose of these
functions is to edit only `string' field).
These functions are evaluated after `math-preview-tex-preprocess-functions',
`math-preview-mathml-preprocess-functions' and
`math-preview-asciimath-preprocess-functions' functions."
:tag "Preprocess functions"
:type '(repeat function)
:safe (lambda (n) (and (listp n)
(-all? #'functionp n))))
(defgroup math-preview-tex nil
"TeX options."
:group 'math-preview
:prefix "math-preview-tex-")
(defcustom math-preview-tex-marks
'(("\\begin{equation}" "\\end{equation}" 0 nil nil)
("\\begin{equation*}" "\\end{equation*}" 0 nil nil)
("\\[" "\\]" 0 nil nil)
("$$" "$$" 0 nil nil))
"Strings marking beginning and end of TeX equation. By default, when searching for equation marks,
the largest match has the priority."
:tag "TeX equation marks"
:type '(repeat :tag "Mark pairs" (list :tag "Mark pair"
(string :tag "Left mark")
(string :tag "Right mark")
(integer :tag "Priority")
(boolean :tag "Left regexp")
(boolean :tag "Right regexp")))
:safe #'math-preview--check-marks)
(defcustom math-preview-tex-marks-inline
'(("\\(" "\\)" 0 nil nil)
("$" "$" 0 nil nil)
("`$" "`$" 0 nil nil))
"Strings marking beginning and end of TeX inline equation."
:tag "TeX equation inline marks"
:type '(repeat :tag "Mark pairs" (list :tag "Mark pair"
(string :tag "Left mark")
(string :tag "Right mark")
(integer :tag "Priority")
(boolean :tag "Left regexp")
(boolean :tag "Right regexp")))
:safe #'math-preview--check-marks)
(defcustom math-preview-tex-preprocess-functions
'((lambda (x) (when (and (s-prefix? "\\begin" (gethash 'lmark x))
(s-prefix? "\\end" (gethash 'rmark x)))
(puthash 'string (gethash 'match x) x))))
"Functions to call on each TeX string.
Functions are applied in chain from left to right (or from top to bottom, when
in `customize'). Each function accepts one arguments which is a hash table
with fields: `match' matched string including marks; `string' matched string
without marks; `type' equation type (`tex', `mathml' or `asciimath');
`inline' equation inline flag; `lmark' and `rmark' are left and right marks
respectively. User may modify `string', `inline' and `type' fields in place to
influence further equation processing (although the intended purpose of these
functions is to edit only `string' field).
These functions are evaluated before `math-preview-preprocess-functions'
functions."
:tag "Preprocess TeX functions"
:type '(repeat function)
:safe (lambda (n) (and (listp n)
(-all? #'functionp n))))
(defcustom math-preview-tex-macros
`(("ddx" . ("\\frac{d#2}{d#1}" 2 "t")))
"List of predefined macros.
`\\' in the name of the macro must be omitted. If macro does not have any
arguments, then macro definition is a string. If macro have arguments, then
macro definition is a list, where first item is a definition string, second
item is a number of macro arguments and third argument is the optional default
value for the first argument. More information at the page
http://docs.mathjax.org/en/latest/input/tex/macros.html#tex-macros and
http://docs.mathjax.org/en/latest/input/tex/extensions/configmacros.html."
:tag "Macro list"
:type '(alist :key-type (string :tag "Name")
:value-type (choice :tag "Definition"
(string :tag "Without arguments")
(list :tag "With arguments"
(string :tag "Definition")
(integer :tag "Number of arguments")
(choice :tag "Default value"
(const :tag "None" nil)
(string :tag "Default value")))
(list :tag "Template based"
(string :tag "Definition")
(integer :tag "Number of arguments")
(repeat :tag "Templates"
(string :tag "Template")))))
:safe (lambda (n) (and (listp n)
(-all? (lambda (x) (and (stringp (car x))
(or (stringp (cdr x))
(listp (cdr x)))))
n))))
(defcustom math-preview-tex-environments
`(("braced" . ("\\left\\{" "\\right\\}")))
"List of predefined environments.
`\\' in the name of the macro must be omitted. If environment does not have
any arguments, then environment definition is a pair of strings. If environment
have arguments, then environment definition is a list, where first two items
are pair of definition strings, third item is a number of environment
arguments and fourth argument is the optional default value for the first
argument. More information at the page
http://docs.mathjax.org/en/latest/input/tex/environments.html#tex-environments
and
http://docs.mathjax.org/en/latest/input/tex/extensions/configenvironments.html."
:tag "Environment list"
:type '(alist :key-type (string :tag "Name")
:value-type (choice :tag "Definition"
(list :tag "Without arguments"
(string :tag "Before")
(string :tag "After "))
(list :tag "With arguments"
(string :tag "Before")
(string :tag "After ")
(integer :tag "Number of arguments")
(choice :tag "Default value"
(const :tag "None" nil)
(string :tag "Default value")))))
:safe (lambda (n) (and (listp n)
(-all? (lambda (x) (and (stringp (car x))
(listp (cdr x))))
n))))
(defgroup math-preview-tex-packages nil
"TeX package options."
:group 'math-preview-tex
:prefix "math-preview-tex-package-")
(defcustom math-preview-tex-default-packages '("autoload" "ams" "cancel" "color")
"List of default `TeX' packages to load.
This array lists the names of the packages (extensions) that should be
initialized by the `TeX' input processor. Packages not in this list must
be loaded using `\\require{}' macro or via `autoload' mechanism.
Extension list is available at the page
http://docs.mathjax.org/en/latest/input/tex/extensions/index.html.
`base', `require', `newcommand' and `configmacros' are always loaded."
:tag "Default TeX packages"
:type '(repeat string)
:safe (lambda (l) (-all? 'stringp l)))
(defgroup math-preview-tex-packages-ams nil
"TeX ams package options.
http://docs.mathjax.org/en/latest/input/tex/extensions/ams.html"
:group 'math-preview-tex-packages
:prefix "math-preview-tex-package-ams-")
(defcustom math-preview-tex-packages-ams-multline-width "100%"
"The width to use for multline environments."
:tag "Multline width"
:type 'string
:safe #'stringp)
(defcustom math-preview-tex-packages-ams-multline-indent "1em"
"The margin to use on both sides of multline environments."
:tag "Multline indent"
:type 'string
:safe #'stringp)
(defgroup math-preview-tex-packages-amscd nil
"TeX amscd package options.
http://docs.mathjax.org/en/latest/input/tex/extensions/amscd.html"
:group 'math-preview-tex-packages
:prefix "math-preview-tex-package-amscd-")
(defcustom math-preview-tex-packages-amsdc-colspace "5pt"
"Column space.
This gives the amount of space to use between columns in the commutative
diagram."
:tag "Column space"
:type 'string
:safe #'stringp)
(defcustom math-preview-tex-packages-amsdc-rowspace "5pt"
"Row space.
This gives the amount of space to use between rows in the commutative
diagram."
:tag "Row space"
:type 'string
:safe #'stringp)
(defcustom math-preview-tex-packages-amsdc-harrowsize "2.75em"
"Horizontal arrow size.
This gives the minimum size for horizontal arrows in the commutative
diagram."
:tag "Horizontal arrow size"
:type 'string
:safe #'stringp)
(defcustom math-preview-tex-packages-amsdc-varrowsize "2.75em"
"Vertical arrow size.
This gives the minimum size for vertical arrows in the commutative
diagram."
:tag "Vertical arrow size"
:type 'string
:safe #'stringp)
(defcustom math-preview-tex-packages-amsdc-hide-horizontal-labels nil
"Hide horizontal labels.
This determines whether horizontal arrows with labels above or below
will use `\\smash' in order to hide the height of the labels.
\(Labels above or below horizontal arrows can cause excess space between rows,
so setting this to true can improve the look of the diagram.)"
:tag "Hide horizontal labels"
:type 'boolean
:safe t)
(defgroup math-preview-tex-packages-autoload nil
"TeX autoload package options.
http://docs.mathjax.org/en/latest/input/tex/extensions/autoload.html"
:group 'math-preview-tex-packages
:prefix "math-preview-tex-package-autoload-")
(defcustom math-preview-tex-packages-autoload-packages
'(("action" . ("toggle" "mathtip" "texttip"))
("amscd" . (() ("CD")))
("bbox" . ("bbox"))
("boldsymbol" . ("boldsymbol"))
("braket" . ("bra" "ket" "braket" "set" "Bra" "Ket" "Braket" "Set" "ketbra" "Ketbra"))
("cancel" . ("cancel" "bcancel" "xcancel" "cancelto"))
("color" . ("color" "definecolor" "textcolor" "colorbox" "fcolorbox"))
("enclose" . ("enclose"))
("extpfeil" . ("xtwoheadrightarrow" "xtwoheadleftarrow" "xmapsto"
"xlongequal" "xtofrom" "Newextarrow"))
("html" . ("href" "class" "style" "cssId"))
("mhchem" . ("ce" "pu"))
("unicode" . ("unicode"))
("upgreek" . ("upalpha" "upbeta" "upchi" "updelta" "Updelta" "upepsilon"
"upeta" "upgamma" "Upgamma" "upiota" "upkappa" "uplambda"
"Uplambda" "upmu" "upnu" "upomega" "Upomega" "upomicron"
"upphi" "Upphi" "uppi" "Uppi" "uppsi" "Uppsi" "uprho"
"upsigma" "Upsigma" "uptau" "uptheta" "Uptheta" "upupsilon"
"Upupsilon" "upvarepsilon" "upvarphi" "upvarpi" "upvarrho"
"upvarsigma" "upvartheta" "upxi" "Upxi" "upzeta"))
("verb" . ("verb")))
"Auto-loading macros and environments.
Adding the autoload extension to the packages array defines an
auto-load sub-block to the `TeX' configuration block. This block
contains key: value pairs where the key is a `TeX' package name,
and the value is an array of macros that cause that package to be loaded,
or an array consisting of two arrays, the first giving names of macros and
the second names of environments; the first time any of them are used,
the extension will be loaded automatically."
:tag "Packages"
:type '(alist :tag "Package"
:key-type (string :tag "Name")
:value-type (choice (repeat :tag "Macros"
(string :tag "Macro"))
(list :tag "Macros and Environments"
(repeat :tag "Macros"
(string :tag "Macro"))
(repeat :tag "Environments"
(string :tag "Environment")))))
:safe (lambda (n) (and (listp n)
(-all? (lambda (x) (and (stringp (car x))
(listp (cdr x))
(or (-all? #'stringp (cdr x))
(and (listp (-first-item (cdr x)))
(listp (-second-item (cdr x)))
(-all? #'stringp (-first-item (cdr x)))
(-all? #'stringp (-second-item (cdr x)))))))
n))))
(defgroup math-preview-tex-packages-physics nil
"TeX physics package options.
http://docs.mathjax.org/en/latest/input/tex/extensions/physics.html"
:group 'math-preview-tex-packages
:prefix "math-preview-tex-package-physics-")
(defcustom math-preview-tex-packages-physics-italicdiff nil
"Italic diff.
This corresponds to the `italicdiff' option of the `physics'
`LaTeX' package to use italic form for the `d' in the `\differential' and
`\derivative' commands."
:tag "Italic diff"
:type 'boolean
:safe t)
(defcustom math-preview-tex-packages-physics-arrowdel nil
"Arrow del.
This corresponds to the `arrowdel' option of the `physics'
`LaTeX' package to use vector notation over the `nabla' symbol."
:tag "Arrow del"
:type 'boolean
:safe t)
(defgroup math-preview-mathml nil
"MathML options."
:group 'math-preview
:prefix "math-preview-mathml-")
(defcustom math-preview-mathml-marks
'(("