mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-10 13:38:39 +00:00
* Support Reaction Arrows This PR is written to supply reaction arrows for a future `mhchem` extension. `mhchem` uses seven reaction arrows. Four of them correspond to extensible arrows already available in KaTeX. This PR creates the other three. These arrows will also be useful to chemists writing about reactions when `mhchem` is unavailable. Three new extensible arrows are introduced: `\xrightleftarrows`, `\xrightequilibrium`, and `\xleftequilibrium`. These names are not `mhchem`’s user-facing function names. In `mhchem`, users would call these arrows with syntax such as: `\ce{A<-->B}`, or `\ce{A<=>>B}`, or `\ce{A<<=>B}`. I’ve provided names that look more like the other extensible arrow names. That’s probably worth some discussion. * generate screenshots for ReactionArrows * Increase overlap in arrow shaft To prevent a small gap when rendering in very large font sizes. * Adjust upper text vert alignment. Add warning. * Limit alignment adjustment to \xleftequilibrium * generate screenshots for reaction arrows
301 lines
7.1 KiB
JavaScript
301 lines
7.1 KiB
JavaScript
// @flow
|
|
/** Include this to ensure that all functions are defined. */
|
|
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});
|
|
};
|
|
|
|
// TODO(kevinb): have functions return an object and call defineFunction with
|
|
// that object in this file instead of relying on side-effects.
|
|
import "./functions/sqrt";
|
|
|
|
import "./functions/color";
|
|
|
|
import "./functions/text";
|
|
|
|
import "./functions/enclose";
|
|
|
|
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 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";
|
|
|
|
import "./functions/sizing";
|
|
|
|
import "./functions/styling";
|
|
|
|
import "./functions/font";
|
|
|
|
import "./functions/accent";
|
|
|
|
// 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
|
|
import "./functions/accentunder";
|
|
|
|
// 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",
|
|
// The next 3 functions are here to support the mhchem extension.
|
|
// Direct use of these functions is discouraged and may break someday.
|
|
"\\xrightleftarrows", "\\xrightequilibrium",
|
|
"\\xleftequilibrium",
|
|
], {
|
|
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,
|
|
};
|
|
});
|
|
|
|
// 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),
|
|
};
|
|
});
|
|
|
|
import "./functions/verb";
|
|
|
|
// Hyperlinks
|
|
import "./functions/href";
|
|
|
|
// MathChoice
|
|
import "./functions/mathchoice";
|