Files
KaTeX/src/macros.js
Ron Kok 97d6229e9b Add vertical kern to \vdots (#1402)
* Add vertical kern to \vdots

Fixes #620.

* Replaced screenshots
2018-06-03 15:46:51 -04:00

622 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @flow
/**
* Predefined macros for KaTeX.
* This can be used to define some commands in terms of others.
*/
import fontMetricsData from "../submodules/katex-fonts/fontMetricsData";
import symbols from "./symbols";
import utils from "./utils";
import {Token} from "./Token";
import ParseError from "./ParseError";
import type Namespace from "./Namespace";
import type {Mode} from "./types";
/**
* Provides context to macros defined by functions. Implemented by
* MacroExpander.
*/
export interface MacroContextInterface {
mode: Mode;
/**
* Object mapping macros to their expansions.
*/
macros: Namespace<MacroDefinition>;
/**
* Returns the topmost token on the stack, without expanding it.
* Similar in behavior to TeX's `\futurelet`.
*/
future(): Token;
/**
* Expand the next token only once (if possible), and return the resulting
* top token on the stack (without removing anything from the stack).
* Similar in behavior to TeX's `\expandafter\futurelet`.
*/
expandAfterFuture(): Token;
/**
* Consume the specified number of arguments from the token stream,
* and return the resulting array of arguments.
*/
consumeArgs(numArgs: number): Token[][];
}
/** Macro tokens (in reverse order). */
export type MacroExpansion = {tokens: Token[], numArgs: number};
export type MacroDefinition = string | MacroExpansion |
(MacroContextInterface => (string | MacroExpansion));
export type MacroMap = {[string]: MacroDefinition};
const builtinMacros: MacroMap = {};
export default builtinMacros;
// This function might one day accept an additional argument and do more things.
export function defineMacro(name: string, body: MacroDefinition) {
builtinMacros[name] = body;
}
//////////////////////////////////////////////////////////////////////
// macro tools
// LaTeX's \@firstoftwo{#1}{#2} expands to #1, skipping #2
// TeX source: \long\def\@firstoftwo#1#2{#1}
defineMacro("\\@firstoftwo", function(context) {
const args = context.consumeArgs(2);
return {tokens: args[0], numArgs: 0};
});
// LaTeX's \@secondoftwo{#1}{#2} expands to #2, skipping #1
// TeX source: \long\def\@secondoftwo#1#2{#2}
defineMacro("\\@secondoftwo", function(context) {
const args = context.consumeArgs(2);
return {tokens: args[1], numArgs: 0};
});
// LaTeX's \@ifnextchar{#1}{#2}{#3} looks ahead to the next (unexpanded)
// symbol. If it matches #1, then the macro expands to #2; otherwise, #3.
// Note, however, that it does not consume the next symbol in either case.
defineMacro("\\@ifnextchar", function(context) {
const args = context.consumeArgs(3); // symbol, if, else
const nextToken = context.future();
if (args[0].length === 1 && args[0][0].text === nextToken.text) {
return {tokens: args[1], numArgs: 0};
} else {
return {tokens: args[2], numArgs: 0};
}
});
// LaTeX's \@ifstar{#1}{#2} looks ahead to the next (unexpanded) symbol.
// If it is `*`, then it consumes the symbol, and the macro expands to #1;
// otherwise, the macro expands to #2 (without consuming the symbol).
// TeX source: \def\@ifstar#1{\@ifnextchar *{\@firstoftwo{#1}}}
defineMacro("\\@ifstar", "\\@ifnextchar *{\\@firstoftwo{#1}}");
// LaTeX's \TextOrMath{#1}{#2} expands to #1 in text mode, #2 in math mode
defineMacro("\\TextOrMath", function(context) {
const args = context.consumeArgs(2);
if (context.mode === 'text') {
return {tokens: args[0], numArgs: 0};
} else {
return {tokens: args[1], numArgs: 0};
}
});
// Basic support for global macro definitions:
// \gdef\macro{expansion}
// \gdef\macro#1{expansion}
// \gdef\macro#1#2{expansion}
// \gdef\macro#1#2#3#4#5#6#7#8#9{expansion}
const def = (context, global: boolean) => {
let arg = context.consumeArgs(1)[0];
if (arg.length !== 1) {
throw new ParseError("\\gdef's first argument must be a macro name");
}
const name = arg[0].text;
// Count argument specifiers, and check they are in the order #1 #2 ...
let numArgs = 0;
arg = context.consumeArgs(1)[0];
while (arg.length === 1 && arg[0].text === "#") {
arg = context.consumeArgs(1)[0];
if (arg.length !== 1) {
throw new ParseError(`Invalid argument number length "${arg.length}"`);
}
if (!(/^[1-9]$/.test(arg[0].text))) {
throw new ParseError(`Invalid argument number "${arg[0].text}"`);
}
numArgs++;
if (parseInt(arg[0].text) !== numArgs) {
throw new ParseError(`Argument number "${arg[0].text}" out of order`);
}
arg = context.consumeArgs(1)[0];
}
// Final arg is the expansion of the macro
context.macros.set(name, {
tokens: arg,
numArgs,
}, global);
return '';
};
defineMacro("\\gdef", (context) => def(context, true));
defineMacro("\\def", (context) => def(context, false));
defineMacro("\\global", (context) => {
const next = context.consumeArgs(1)[0];
if (next.length !== 1) {
throw new ParseError("Invalid command after \\global");
}
const command = next[0].text;
if (command === "\\def") {
// \global\def is equivalent to \gdef
return def(context, true);
} else {
throw new ParseError(`Invalid command '${command}' after \\global`);
}
});
//////////////////////////////////////////////////////////////////////
// Grouping
// \let\bgroup={ \let\egroup=}
defineMacro("\\bgroup", "{");
defineMacro("\\egroup", "}");
defineMacro("\\begingroup", "{");
defineMacro("\\endgroup", "}");
// Symbols from latex.ltx:
// \def\lq{`}
// \def\rq{'}
// \def\lbrack{[}
// \def\rbrack{]}
// \def \aa {\r a}
// \def \AA {\r A}
defineMacro("\\lq", "`");
defineMacro("\\rq", "'");
defineMacro("\\lbrack", "[");
defineMacro("\\rbrack", "]");
defineMacro("\\aa", "\\r a");
defineMacro("\\AA", "\\r A");
// \DeclareTextCommandDefault{\textcopyright}{\textcircled{c}}
// \DeclareTextCommandDefault{\textregistered}{\textcircled{%
// \check@mathfonts\fontsize\sf@size\z@\math@fontsfalse\selectfont R}}
// \DeclareRobustCommand{\copyright}{%
// \ifmmode{\nfss@text{\textcopyright}}\else\textcopyright\fi}
defineMacro("\\textcopyright", "\\textcircled{c}");
defineMacro("\\copyright",
"\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}");
defineMacro("\\textregistered", "\\textcircled{\\scriptsize R}");
// Unicode double-struck letters
defineMacro("\u2102", "\\mathbb{C}");
defineMacro("\u210D", "\\mathbb{H}");
defineMacro("\u2115", "\\mathbb{N}");
defineMacro("\u2119", "\\mathbb{P}");
defineMacro("\u211A", "\\mathbb{Q}");
defineMacro("\u211D", "\\mathbb{R}");
defineMacro("\u2124", "\\mathbb{Z}");
defineMacro("\u210E", "\\mathit{h}"); // Planck constant
// Characters omitted from Unicode range 1D4001D7FF
defineMacro("\u212C", "\\mathscr{B}"); // script
defineMacro("\u2130", "\\mathscr{E}");
defineMacro("\u2131", "\\mathscr{F}");
defineMacro("\u210B", "\\mathscr{H}");
defineMacro("\u2110", "\\mathscr{I}");
defineMacro("\u2112", "\\mathscr{L}");
defineMacro("\u2133", "\\mathscr{M}");
defineMacro("\u211B", "\\mathscr{R}");
defineMacro("\u212D", "\\mathfrak{C}"); // Fraktur
defineMacro("\u210C", "\\mathfrak{H}");
defineMacro("\u2128", "\\mathfrak{Z}");
// Unicode middle dot
// The KaTeX fonts do not contain U+00B7. Instead, \cdotp displays
// the dot at U+22C5 and gives it punct spacing.
defineMacro("\u00b7", "\\cdotp");
// \llap and \rlap render their contents in text mode
defineMacro("\\llap", "\\mathllap{\\textrm{#1}}");
defineMacro("\\rlap", "\\mathrlap{\\textrm{#1}}");
defineMacro("\\clap", "\\mathclap{\\textrm{#1}}");
// Unicode stacked relations
defineMacro("\u2258",
"\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}");
defineMacro("\u2259", "\\stackrel{\\tiny\\wedge}{=}");
defineMacro("\u225A", "\\stackrel{\\tiny\\vee}{=}");
defineMacro("\u225B", "\\stackrel{\\scriptsize\\star}{=}");
defineMacro("\u225D", "\\stackrel{\\tiny\\mathrm{def}}{=}");
defineMacro("\u225E", "\\stackrel{\\tiny\\mathrm{m}}{=}");
defineMacro("\u225F", "\\stackrel{\\tiny?}{=}");
// Misc Unicode
defineMacro("\u27C2", "\\perp");
defineMacro("\u203C", "\\mathclose{!\\mkern-0.8mu!}");
defineMacro("\u220C", "\\notni");
//////////////////////////////////////////////////////////////////////
// LaTeX_2ε
// \vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@
// \kern6\p@\hbox{.}\hbox{.}\hbox{.}}}
// We'll call \varvdots, which gets a glyph from symbols.js.
// The zero-width rule gets us an equivalent to the vertical 6pt kern.
defineMacro("\\vdots", "\\mathord{\\varvdots\\rule{0pt}{15pt}}");
defineMacro("\u22ee", "\\vdots");
//////////////////////////////////////////////////////////////////////
// amsmath.sty
// http://mirrors.concertpass.com/tex-archive/macros/latex/required/amsmath/amsmath.pdf
// Italic Greek capital letters. AMS defines these with \DeclareMathSymbol,
// but they are equivalent to \mathit{\Letter}.
defineMacro("\\varGamma", "\\mathit{\\Gamma}");
defineMacro("\\varDelta", "\\mathit{\\Delta}");
defineMacro("\\varTheta", "\\mathit{\\Theta}");
defineMacro("\\varLambda", "\\mathit{\\Lambda}");
defineMacro("\\varXi", "\\mathit{\\Xi}");
defineMacro("\\varPi", "\\mathit{\\Pi}");
defineMacro("\\varSigma", "\\mathit{\\Sigma}");
defineMacro("\\varUpsilon", "\\mathit{\\Upsilon}");
defineMacro("\\varPhi", "\\mathit{\\Phi}");
defineMacro("\\varPsi", "\\mathit{\\Psi}");
defineMacro("\\varOmega", "\\mathit{\\Omega}");
// \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}}
defineMacro("\\boxed", "\\fbox{\\displaystyle{#1}}");
// \def\iff{\DOTSB\;\Longleftrightarrow\;}
// \def\implies{\DOTSB\;\Longrightarrow\;}
// \def\impliedby{\DOTSB\;\Longleftarrow\;}
defineMacro("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;");
defineMacro("\\implies", "\\DOTSB\\;\\Longrightarrow\\;");
defineMacro("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;");
// AMSMath's automatic \dots, based on \mdots@@ macro.
const dotsByToken = {
',': '\\dotsc',
'\\not': '\\dotsb',
// \keybin@ checks for the following:
'+': '\\dotsb',
'=': '\\dotsb',
'<': '\\dotsb',
'>': '\\dotsb',
'-': '\\dotsb',
'*': '\\dotsb',
':': '\\dotsb',
// Symbols whose definition starts with \DOTSB:
'\\DOTSB': '\\dotsb',
'\\coprod': '\\dotsb',
'\\bigvee': '\\dotsb',
'\\bigwedge': '\\dotsb',
'\\biguplus': '\\dotsb',
'\\bigcap': '\\dotsb',
'\\bigcup': '\\dotsb',
'\\prod': '\\dotsb',
'\\sum': '\\dotsb',
'\\bigotimes': '\\dotsb',
'\\bigoplus': '\\dotsb',
'\\bigodot': '\\dotsb',
'\\bigsqcup': '\\dotsb',
'\\And': '\\dotsb',
'\\longrightarrow': '\\dotsb',
'\\Longrightarrow': '\\dotsb',
'\\longleftarrow': '\\dotsb',
'\\Longleftarrow': '\\dotsb',
'\\longleftrightarrow': '\\dotsb',
'\\Longleftrightarrow': '\\dotsb',
'\\mapsto': '\\dotsb',
'\\longmapsto': '\\dotsb',
'\\hookrightarrow': '\\dotsb',
'\\doteq': '\\dotsb',
// Symbols whose definition starts with \mathbin:
'\\mathbin': '\\dotsb',
// Symbols whose definition starts with \mathrel:
'\\mathrel': '\\dotsb',
'\\relbar': '\\dotsb',
'\\Relbar': '\\dotsb',
'\\xrightarrow': '\\dotsb',
'\\xleftarrow': '\\dotsb',
// Symbols whose definition starts with \DOTSI:
'\\DOTSI': '\\dotsi',
'\\int': '\\dotsi',
'\\oint': '\\dotsi',
'\\iint': '\\dotsi',
'\\iiint': '\\dotsi',
'\\iiiint': '\\dotsi',
'\\idotsint': '\\dotsi',
// Symbols whose definition starts with \DOTSX:
'\\DOTSX': '\\dotsx',
};
defineMacro("\\dots", function(context) {
// TODO: If used in text mode, should expand to \textellipsis.
// However, in KaTeX, \textellipsis and \ldots behave the same
// (in text mode), and it's unlikely we'd see any of the math commands
// that affect the behavior of \dots when in text mode. So fine for now
// (until we support \ifmmode ... \else ... \fi).
let thedots = '\\dotso';
const next = context.expandAfterFuture().text;
if (next in dotsByToken) {
thedots = dotsByToken[next];
} else if (next.substr(0, 4) === '\\not') {
thedots = '\\dotsb';
} else if (next in symbols.math) {
if (utils.contains(['bin', 'rel'], symbols.math[next].group)) {
thedots = '\\dotsb';
}
}
return thedots;
});
const spaceAfterDots = {
// \rightdelim@ checks for the following:
')': true,
']': true,
'\\rbrack': true,
'\\}': true,
'\\rbrace': true,
'\\rangle': true,
'\\rceil': true,
'\\rfloor': true,
'\\rgroup': true,
'\\rmoustache': true,
'\\right': true,
'\\bigr': true,
'\\biggr': true,
'\\Bigr': true,
'\\Biggr': true,
// \extra@ also tests for the following:
'$': true,
// \extrap@ checks for the following:
';': true,
'.': true,
',': true,
};
defineMacro("\\dotso", function(context) {
const next = context.future().text;
if (next in spaceAfterDots) {
return "\\ldots\\,";
} else {
return "\\ldots";
}
});
defineMacro("\\dotsc", function(context) {
const next = context.future().text;
// \dotsc uses \extra@ but not \extrap@, instead specially checking for
// ';' and '.', but doesn't check for ','.
if (next in spaceAfterDots && next !== ',') {
return "\\ldots\\,";
} else {
return "\\ldots";
}
});
defineMacro("\\cdots", function(context) {
const next = context.future().text;
if (next in spaceAfterDots) {
return "\\@cdots\\,";
} else {
return "\\@cdots";
}
});
defineMacro("\\dotsb", "\\cdots");
defineMacro("\\dotsm", "\\cdots");
defineMacro("\\dotsi", "\\!\\cdots");
// amsmath doesn't actually define \dotsx, but \dots followed by a macro
// starting with \DOTSX implies \dotso, and then \extra@ detects this case
// and forces the added `\,`.
defineMacro("\\dotsx", "\\ldots\\,");
// \let\DOTSI\relax
// \let\DOTSB\relax
// \let\DOTSX\relax
defineMacro("\\DOTSI", "\\relax");
defineMacro("\\DOTSB", "\\relax");
defineMacro("\\DOTSX", "\\relax");
// Spacing, based on amsmath.sty's override of LaTeX defaults
// \DeclareRobustCommand{\tmspace}[3]{%
// \ifmmode\mskip#1#2\else\kern#1#3\fi\relax}
defineMacro("\\tmspace", "\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax");
// \renewcommand{\,}{\tmspace+\thinmuskip{.1667em}}
// TODO: math mode should use \thinmuskip
defineMacro("\\,", "\\tmspace+{3mu}{.1667em}");
// \let\thinspace\,
defineMacro("\\thinspace", "\\,");
// \renewcommand{\:}{\tmspace+\medmuskip{.2222em}}
// TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu
defineMacro("\\:", "\\tmspace+{4mu}{.2222em}");
// \let\medspace\:
defineMacro("\\medspace", "\\:");
// \renewcommand{\;}{\tmspace+\thickmuskip{.2777em}}
// TODO: math mode should use \thickmuskip = 5mu plus 5mu
defineMacro("\\;", "\\tmspace+{5mu}{.2777em}");
// \let\thickspace\;
defineMacro("\\thickspace", "\\;");
// \renewcommand{\!}{\tmspace-\thinmuskip{.1667em}}
// TODO: math mode should use \thinmuskip
defineMacro("\\!", "\\tmspace-{3mu}{.1667em}");
// \let\negthinspace\!
defineMacro("\\negthinspace", "\\!");
// \newcommand{\negmedspace}{\tmspace-\medmuskip{.2222em}}
// TODO: math mode should use \medmuskip
defineMacro("\\negmedspace", "\\tmspace-{4mu}{.2222em}");
// \newcommand{\negthickspace}{\tmspace-\thickmuskip{.2777em}}
// TODO: math mode should use \thickmuskip
defineMacro("\\negthickspace", "\\tmspace-{5mu}{.277em}");
// \def\enspace{\kern.5em }
defineMacro("\\enspace", "\\kern.5em ");
// \def\enskip{\hskip.5em\relax}
defineMacro("\\enskip", "\\hskip.5em\\relax");
// \def\quad{\hskip1em\relax}
defineMacro("\\quad", "\\hskip1em\\relax");
// \def\qquad{\hskip2em\relax}
defineMacro("\\qquad", "\\hskip2em\\relax");
// \tag@in@display form of \tag
defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren");
defineMacro("\\tag@paren", "\\tag@literal{({#1})}");
defineMacro("\\tag@literal", (context) => {
if (context.macros.get("\\df@tag")) {
throw new ParseError("Multiple \\tag");
}
return "\\gdef\\df@tag{\\text{#1}}";
});
// \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin
// {\operator@font mod}\penalty900
// \mkern5mu\nonscript\mskip-\medmuskip}
// \newcommand{\pod}[1]{\allowbreak
// \if@display\mkern18mu\else\mkern8mu\fi(#1)}
// \renewcommand{\pmod}[1]{\pod{{\operator@font mod}\mkern6mu#1}}
// \newcommand{\mod}[1]{\allowbreak\if@display\mkern18mu
// \else\mkern12mu\fi{\operator@font mod}\,\,#1}
// TODO: math mode should use \medmuskip = 4mu plus 2mu minus 4mu
defineMacro("\\bmod",
"\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}" +
"\\mathbin{\\rm mod}" +
"\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}");
defineMacro("\\pod", "\\allowbreak" +
"\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)");
defineMacro("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}");
defineMacro("\\mod", "\\allowbreak" +
"\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}" +
"{\\rm mod}\\,\\,#1");
//////////////////////////////////////////////////////////////////////
// LaTeX source2e
// \\ defaults to \newline, but changes to \cr within array environment
defineMacro("\\\\", "\\newline");
// \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
// TODO: Doesn't normally work in math mode because \@ fails. KaTeX doesn't
// support \@ yet, so that's omitted, and we add \text so that the result
// doesn't look funny in math mode.
defineMacro("\\TeX", "\\textrm{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}");
// \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
// {\sbox\z@ T%
// \vbox to\ht\z@{\hbox{\check@mathfonts
// \fontsize\sf@size\z@
// \math@fontsfalse\selectfont
// A}%
// \vss}%
// }%
// \kern-.15em%
// \TeX}
// This code aligns the top of the A with the T (from the perspective of TeX's
// boxes, though visually the A appears to extend above slightly).
// We compute the corresponding \raisebox when A is rendered at \scriptsize,
// which is size3, which has a scale factor of 0.7 (see Options.js).
const latexRaiseA = fontMetricsData['Main-Regular']["T".charCodeAt(0)][1] -
0.7 * fontMetricsData['Main-Regular']["A".charCodeAt(0)][1] + "em";
defineMacro("\\LaTeX",
`\\textrm{L\\kern-.36em\\raisebox{${latexRaiseA}}{\\scriptsize A}` +
"\\kern-.15em\\TeX}");
// New KaTeX logo based on tweaking LaTeX logo
defineMacro("\\KaTeX",
`\\textrm{K\\kern-.17em\\raisebox{${latexRaiseA}}{\\scriptsize A}` +
"\\kern-.15em\\TeX}");
// \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace}
// \def\@hspace#1{\hskip #1\relax}
// \def\@hspacer#1{\vrule \@width\z@\nobreak
// \hskip #1\hskip \z@skip}
defineMacro("\\hspace", "\\@ifstar\\@hspacer\\@hspace");
defineMacro("\\@hspace", "\\hskip #1\\relax");
defineMacro("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax");
//////////////////////////////////////////////////////////////////////
// mathtools.sty
//\providecommand\ordinarycolon{:}
defineMacro("\\ordinarycolon", ":");
//\def\vcentcolon{\mathrel{\mathop\ordinarycolon}}
//TODO(edemaine): Not yet centered. Fix via \raisebox or #726
defineMacro("\\vcentcolon", "\\mathrel{\\mathop\\ordinarycolon}");
// \providecommand*\dblcolon{\vcentcolon\mathrel{\mkern-.9mu}\vcentcolon}
defineMacro("\\dblcolon",
"\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}");
// \providecommand*\coloneqq{\vcentcolon\mathrel{\mkern-1.2mu}=}
defineMacro("\\coloneqq", "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}");
// \providecommand*\Coloneqq{\dblcolon\mathrel{\mkern-1.2mu}=}
defineMacro("\\Coloneqq", "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}");
// \providecommand*\coloneq{\vcentcolon\mathrel{\mkern-1.2mu}\mathrel{-}}
defineMacro("\\coloneq",
"\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}");
// \providecommand*\Coloneq{\dblcolon\mathrel{\mkern-1.2mu}\mathrel{-}}
defineMacro("\\Coloneq",
"\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}");
// \providecommand*\eqqcolon{=\mathrel{\mkern-1.2mu}\vcentcolon}
defineMacro("\\eqqcolon", "\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}");
// \providecommand*\Eqqcolon{=\mathrel{\mkern-1.2mu}\dblcolon}
defineMacro("\\Eqqcolon", "\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}");
// \providecommand*\eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\vcentcolon}
defineMacro("\\eqcolon",
"\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}");
// \providecommand*\Eqcolon{\mathrel{-}\mathrel{\mkern-1.2mu}\dblcolon}
defineMacro("\\Eqcolon",
"\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}");
// \providecommand*\colonapprox{\vcentcolon\mathrel{\mkern-1.2mu}\approx}
defineMacro("\\colonapprox",
"\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}");
// \providecommand*\Colonapprox{\dblcolon\mathrel{\mkern-1.2mu}\approx}
defineMacro("\\Colonapprox",
"\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}");
// \providecommand*\colonsim{\vcentcolon\mathrel{\mkern-1.2mu}\sim}
defineMacro("\\colonsim", "\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}");
// \providecommand*\Colonsim{\dblcolon\mathrel{\mkern-1.2mu}\sim}
defineMacro("\\Colonsim", "\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}");
// Some Unicode characters are implemented with macros to mathtools functions.
defineMacro("\u2254", "\\coloneqq"); // :=
defineMacro("\u2255", "\\eqqcolon"); // =:
defineMacro("\u2A74", "\\Coloneqq"); // ::=
//////////////////////////////////////////////////////////////////////
// colonequals.sty
// Alternate names for mathtools's macros:
defineMacro("\\ratio", "\\vcentcolon");
defineMacro("\\coloncolon", "\\dblcolon");
defineMacro("\\colonequals", "\\coloneqq");
defineMacro("\\coloncolonequals", "\\Coloneqq");
defineMacro("\\equalscolon", "\\eqqcolon");
defineMacro("\\equalscoloncolon", "\\Eqqcolon");
defineMacro("\\colonminus", "\\coloneq");
defineMacro("\\coloncolonminus", "\\Coloneq");
defineMacro("\\minuscolon", "\\eqcolon");
defineMacro("\\minuscoloncolon", "\\Eqcolon");
// \colonapprox name is same in mathtools and colonequals.
defineMacro("\\coloncolonapprox", "\\Colonapprox");
// \colonsim name is same in mathtools and colonequals.
defineMacro("\\coloncolonsim", "\\Colonsim");
// Additional macros, implemented by analogy with mathtools definitions:
defineMacro("\\simcolon",
"\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}");
defineMacro("\\simcoloncolon",
"\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}");
defineMacro("\\approxcolon",
"\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}");
defineMacro("\\approxcoloncolon",
"\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}");
// Present in newtxmath, pxfonts and txfonts
// TODO: The unicode character U+220C ∌ should be added to the font, and this
// macro turned into a propper defineSymbol in symbols.js. That way, the
// MathML result will be much cleaner.
defineMacro("\\notni", "\\not\\ni");
defineMacro("\\limsup", "\\DOTSB\\mathop{\\operatorname{lim\\,sup}}\\limits");
defineMacro("\\liminf", "\\DOTSB\\mathop{\\operatorname{lim\\,inf}}\\limits");