Files
KaTeX/src/functions.js
Kevin Barabash 7229f02d8f Implement correct macros for liminf and limsup, fixes #111 (#887)
* Implement correct macros for liminf and limsup, fixes #111

* fix nits
2017-12-26 17:11:10 -07:00

477 lines
11 KiB
JavaScript

// @flow
/** Include this to ensure that all functions are defined. */
import utils from "./utils";
import ParseError from "./ParseError";
import ParseNode from "./ParseNode";
import {
default as _defineFunction,
ordargument,
_functions,
} from "./defineFunction";
import type {FunctionPropSpec, FunctionHandler} from "./defineFunction" ;
// WARNING: New functions should be added to src/functions and imported here.
const functions = _functions;
export default functions;
// Define a convenience function that mimcs the old semantics of defineFunction
// to support existing code so that we can migrate it a little bit at a time.
const defineFunction = function(
names: string[],
props: FunctionPropSpec,
handler: ?FunctionHandler, // null only if handled in parser
) {
_defineFunction({names, props, handler});
};
// A normal square root
defineFunction(["\\sqrt"], {
numArgs: 1,
numOptionalArgs: 1,
}, function(context, args, optArgs) {
const index = optArgs[0];
const body = args[0];
return {
type: "sqrt",
body: body,
index: index,
};
});
import "./functions/color";
import "./functions/text";
// \color is handled in Parser.js's parseImplicitGroup
defineFunction(["\\color"], {
numArgs: 1,
allowedInText: true,
greediness: 3,
argTypes: ["color"],
}, null);
// colorbox
defineFunction(["\\colorbox"], {
numArgs: 2,
allowedInText: true,
greediness: 3,
argTypes: ["color", "text"],
}, function(context, args) {
const color = args[0];
const body = args[1];
return {
type: "enclose",
label: context.funcName,
backgroundColor: color,
body: body,
};
});
// fcolorbox
defineFunction(["\\fcolorbox"], {
numArgs: 3,
allowedInText: true,
greediness: 3,
argTypes: ["color", "color", "text"],
}, function(context, args) {
const borderColor = args[0];
const backgroundColor = args[1];
const body = args[2];
return {
type: "enclose",
label: context.funcName,
backgroundColor: backgroundColor,
borderColor: borderColor,
body: body,
};
});
import "./functions/overline";
import "./functions/underline";
import "./functions/rule";
import "./functions/kern";
import "./functions/phantom";
// Math class commands except \mathop
defineFunction([
"\\mathord", "\\mathbin", "\\mathrel", "\\mathopen",
"\\mathclose", "\\mathpunct", "\\mathinner",
], {
numArgs: 1,
}, function(context, args) {
const body = args[0];
return {
type: "mclass",
mclass: "m" + context.funcName.substr(5),
value: ordargument(body),
};
});
// Build a relation by placing one symbol on top of another
defineFunction(["\\stackrel"], {
numArgs: 2,
}, function(context, args) {
const top = args[0];
const bottom = args[1];
const bottomop = new ParseNode("op", {
type: "op",
limits: true,
alwaysHandleSupSub: true,
symbol: false,
value: ordargument(bottom),
}, bottom.mode);
const supsub = new ParseNode("supsub", {
base: bottomop,
sup: top,
sub: null,
}, top.mode);
return {
type: "mclass",
mclass: "mrel",
value: [supsub],
};
});
import "./functions/mod";
const fontAliases = {
"\\Bbb": "\\mathbb",
"\\bold": "\\mathbf",
"\\frak": "\\mathfrak",
"\\bm": "\\boldsymbol",
};
const singleCharIntegrals: {[string]: string} = {
"\u222b": "\\int",
"\u222c": "\\iint",
"\u222d": "\\iiint",
"\u222e": "\\oint",
};
// There are 2 flags for operators; whether they produce limits in
// displaystyle, and whether they are symbols and should grow in
// displaystyle. These four groups cover the four possible choices.
// No limits, not symbols
defineFunction([
"\\arcsin", "\\arccos", "\\arctan", "\\arctg", "\\arcctg",
"\\arg", "\\ch", "\\cos", "\\cosec", "\\cosh", "\\cot", "\\cotg",
"\\coth", "\\csc", "\\ctg", "\\cth", "\\deg", "\\dim", "\\exp",
"\\hom", "\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin",
"\\sinh", "\\sh", "\\tan", "\\tanh", "\\tg", "\\th",
], {
numArgs: 0,
}, function(context) {
return {
type: "op",
limits: false,
symbol: false,
body: context.funcName,
};
});
// Limits, not symbols
defineFunction([
"\\det", "\\gcd", "\\inf", "\\lim", "\\max", "\\min", "\\Pr", "\\sup",
], {
numArgs: 0,
}, function(context) {
return {
type: "op",
limits: true,
symbol: false,
body: context.funcName,
};
});
// No limits, symbols
defineFunction([
"\\int", "\\iint", "\\iiint", "\\oint", "\u222b", "\u222c",
"\u222d", "\u222e",
], {
numArgs: 0,
}, function(context) {
let fName = context.funcName;
if (fName.length === 1) {
fName = singleCharIntegrals[fName];
}
return {
type: "op",
limits: false,
symbol: true,
body: fName,
};
});
import "./functions/op";
import "./functions/operatorname";
import "./functions/genfrac";
import "./functions/lap";
import "./functions/smash";
import "./functions/delimsizing";
// Sizing functions (handled in Parser.js explicitly, hence no handler)
defineFunction([
"\\tiny", "\\scriptsize", "\\footnotesize", "\\small",
"\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
], {numArgs: 0}, null);
// Style changing functions (handled in Parser.js explicitly, hence no
// handler)
defineFunction([
"\\displaystyle", "\\textstyle", "\\scriptstyle",
"\\scriptscriptstyle",
], {numArgs: 0}, null);
// Old font changing functions
defineFunction([
"\\rm", "\\sf", "\\tt", "\\bf", "\\it", //"\\sl", "\\sc",
], {numArgs: 0}, null);
defineFunction([
// styles
"\\mathrm", "\\mathit", "\\mathbf", "\\boldsymbol",
// families
"\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf",
"\\mathtt",
// aliases
"\\Bbb", "\\bold", "\\frak", "\\bm",
], {
numArgs: 1,
greediness: 2,
}, function(context, args) {
const body = args[0];
let func = context.funcName;
if (func in fontAliases) {
func = fontAliases[func];
}
return {
type: "font",
font: func.slice(1),
body: body,
};
});
// Accents
defineFunction([
"\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
"\\check", "\\hat", "\\vec", "\\dot",
"\\widehat", "\\widetilde", "\\overrightarrow", "\\overleftarrow",
"\\Overrightarrow", "\\overleftrightarrow", "\\overgroup",
"\\overlinesegment", "\\overleftharpoon", "\\overrightharpoon",
], {
numArgs: 1,
}, function(context, args) {
const base = args[0];
const isStretchy = !utils.contains([
"\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
"\\check", "\\hat", "\\vec", "\\dot",
], context.funcName);
const isShifty = !isStretchy || utils.contains([
"\\widehat", "\\widetilde",
], context.funcName);
return {
type: "accent",
label: context.funcName,
isStretchy: isStretchy,
isShifty: isShifty,
base: base,
};
});
// Text-mode accents
defineFunction([
"\\'", "\\`", "\\^", "\\~", "\\=", "\\u", "\\.", '\\"',
"\\r", "\\H", "\\v",
], {
numArgs: 1,
allowedInText: true,
allowedInMath: false,
}, function(context, args) {
const base = args[0];
return {
type: "accent",
label: context.funcName,
isStretchy: false,
isShifty: true,
base: base,
};
});
// Horizontal stretchy braces
defineFunction([
"\\overbrace", "\\underbrace",
], {
numArgs: 1,
}, function(context, args) {
const base = args[0];
return {
type: "horizBrace",
label: context.funcName,
isOver: /^\\over/.test(context.funcName),
base: base,
};
});
// Stretchy accents under the body
defineFunction([
"\\underleftarrow", "\\underrightarrow", "\\underleftrightarrow",
"\\undergroup", "\\underlinesegment", "\\utilde",
], {
numArgs: 1,
}, function(context, args) {
const base = args[0];
return {
type: "accentUnder",
label: context.funcName,
base: base,
};
});
// Stretchy arrows with an optional argument
defineFunction([
"\\xleftarrow", "\\xrightarrow", "\\xLeftarrow", "\\xRightarrow",
"\\xleftrightarrow", "\\xLeftrightarrow", "\\xhookleftarrow",
"\\xhookrightarrow", "\\xmapsto", "\\xrightharpoondown",
"\\xrightharpoonup", "\\xleftharpoondown", "\\xleftharpoonup",
"\\xrightleftharpoons", "\\xleftrightharpoons", "\\xlongequal",
"\\xtwoheadrightarrow", "\\xtwoheadleftarrow", "\\xtofrom",
], {
numArgs: 1,
numOptionalArgs: 1,
}, function(context, args, optArgs) {
const below = optArgs[0];
const body = args[0];
return {
type: "xArrow", // x for extensible
label: context.funcName,
body: body,
below: below,
};
});
// enclose
defineFunction(["\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\fbox"], {
numArgs: 1,
}, function(context, args) {
const body = args[0];
return {
type: "enclose",
label: context.funcName,
body: body,
};
});
// Infix generalized fractions
defineFunction(["\\over", "\\choose", "\\atop"], {
numArgs: 0,
infix: true,
}, function(context) {
let replaceWith;
switch (context.funcName) {
case "\\over":
replaceWith = "\\frac";
break;
case "\\choose":
replaceWith = "\\binom";
break;
case "\\atop":
replaceWith = "\\\\atopfrac";
break;
default:
throw new Error("Unrecognized infix genfrac command");
}
return {
type: "infix",
replaceWith: replaceWith,
token: context.token,
};
});
// Row breaks for aligned data
defineFunction(["\\\\", "\\cr"], {
numArgs: 0,
numOptionalArgs: 1,
argTypes: ["size"],
}, function(context, args, optArgs) {
const size = optArgs[0];
return {
type: "cr",
size: size,
};
});
// Environment delimiters
defineFunction(["\\begin", "\\end"], {
numArgs: 1,
argTypes: ["text"],
}, function(context, args) {
const nameGroup = args[0];
if (nameGroup.type !== "ordgroup") {
throw new ParseError("Invalid environment name", nameGroup);
}
let name = "";
for (let i = 0; i < nameGroup.value.length; ++i) {
name += nameGroup.value[i].value;
}
return {
type: "environment",
name: name,
nameGroup: nameGroup,
};
});
// Box manipulation
defineFunction(["\\raisebox"], {
numArgs: 2,
argTypes: ["size", "text"],
allowedInText: true,
}, function(context, args) {
const amount = args[0];
const body = args[1];
return {
type: "raisebox",
dy: amount,
body: body,
value: ordargument(body),
};
});
// \verb and \verb* are dealt with directly in Parser.js.
// If we end up here, it's because of a failure to match the two delimiters
// in the regex in Lexer.js. LaTeX raises the following error when \verb is
// terminated by end of line (or file).
defineFunction(["\\verb"], {
numArgs: 0,
allowedInText: true,
}, function(context) {
throw new ParseError(
"\\verb ended by end of line instead of matching delimiter");
});
// Hyperlinks
import "./functions/href";
// MathChoice
import "./functions/mathchoice";