mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 19:28:39 +00:00
Export htmlBuilder from accent, horizBrace, and op for supsub. (#1349)
* Export htmlBuilder from accent, horizBrace, and op for supsub. * Addressed review comments.
This commit is contained in:
@@ -290,13 +290,15 @@ type ParseNodeTypes = {
|
||||
* typing. Throws if the node's type does not match.
|
||||
*/
|
||||
export function assertNodeType<NODETYPE: NodeType>(
|
||||
node: ParseNode<*>,
|
||||
// The union allows either ParseNode<*> or the union of two specific nodes.
|
||||
node: ?ParseNode<*> | ParseNode<*>,
|
||||
type: NODETYPE,
|
||||
): ParseNode<NODETYPE> {
|
||||
const typedNode = checkNodeType(node, type);
|
||||
if (!typedNode) {
|
||||
throw new Error(
|
||||
`Expected node of type ${type}, but got node of type ${node.type}`);
|
||||
`Expected node of type ${type}, but got ` +
|
||||
(node ? `node of type ${node.type}` : String(node)));
|
||||
}
|
||||
return typedNode;
|
||||
}
|
||||
@@ -306,10 +308,11 @@ export function assertNodeType<NODETYPE: NodeType>(
|
||||
* returns null.
|
||||
*/
|
||||
export function checkNodeType<NODETYPE: NodeType>(
|
||||
node: ParseNode<*>,
|
||||
// The union allows either ParseNode<*> or the union of two specific nodes.
|
||||
node: ?ParseNode<*> | ParseNode<*>,
|
||||
type: NODETYPE,
|
||||
): ?ParseNode<NODETYPE> {
|
||||
return node.type === type ?
|
||||
return node && node.type === type ?
|
||||
(node: ParseNode<NODETYPE>) :
|
||||
null;
|
||||
}
|
||||
|
@@ -32,6 +32,12 @@ export type MathMLBuilder<NODETYPE> = (
|
||||
options: Options,
|
||||
) => MathNode | TextNode | domTree.documentFragment;
|
||||
|
||||
// More general version of `HtmlBuilder` for nodes (e.g. \sum, accent types)
|
||||
// whose presence impacts super/subscripting. In this case, ParseNode<"supsub">
|
||||
// delegates its HTML building to the HtmlBuilder corresponding to these nodes.
|
||||
export type HtmlBuilderSupSub<NODETYPE> =
|
||||
(ParseNode<"supsub"> | ParseNode<NODETYPE>, Options) => HtmlDomNode;
|
||||
|
||||
export type FunctionPropSpec = {
|
||||
// The number of arguments the function takes.
|
||||
numArgs: number,
|
||||
|
@@ -4,16 +4,24 @@ import buildCommon from "../buildCommon";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
import utils from "../utils";
|
||||
import stretchy from "../stretchy";
|
||||
import {assertNodeType, checkNodeType} from "../ParseNode";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
|
||||
const htmlBuilder = (group, options) => {
|
||||
// Accents are handled in the TeXbook pg. 443, rule 12.
|
||||
let base = group.value.base;
|
||||
import type ParseNode from "../ParseNode";
|
||||
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
|
||||
|
||||
let supsubGroup;
|
||||
if (group.type === "supsub") {
|
||||
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "accent", but
|
||||
// also "supsub" since an accent can affect super/subscripting.
|
||||
export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
|
||||
// Accents are handled in the TeXbook pg. 443, rule 12.
|
||||
let base: ParseNode<*>;
|
||||
let group: ParseNode<"accent">;
|
||||
|
||||
const supSub = checkNodeType(grp, "supsub");
|
||||
let supSubGroup;
|
||||
if (supSub) {
|
||||
// If our base is a character box, and we have superscripts and
|
||||
// subscripts, the supsub will defer to us. In particular, we want
|
||||
// to attach the superscripts and subscripts to the inner body (so
|
||||
@@ -22,18 +30,19 @@ const htmlBuilder = (group, options) => {
|
||||
// sticking the base of the accent into the base of the supsub, and
|
||||
// rendering that, while keeping track of where the accent is.
|
||||
|
||||
// The supsub group is the group that was passed in
|
||||
const supsub = group;
|
||||
// The real accent group is the base of the supsub group
|
||||
group = supsub.value.base;
|
||||
group = assertNodeType(supSub.value.base, "accent");
|
||||
// The character box is the base of the accent group
|
||||
base = group.value.base;
|
||||
// Stick the character box into the base of the supsub group
|
||||
supsub.value.base = base;
|
||||
supSub.value.base = base;
|
||||
|
||||
// Rerender the supsub group with its new base, and store that
|
||||
// result.
|
||||
supsubGroup = html.buildGroup(supsub, options);
|
||||
supSubGroup = html.buildGroup(supSub, options);
|
||||
} else {
|
||||
group = assertNodeType(grp, "accent");
|
||||
base = group.value.base;
|
||||
}
|
||||
|
||||
// Build the base group
|
||||
@@ -153,25 +162,25 @@ const htmlBuilder = (group, options) => {
|
||||
const accentWrap =
|
||||
buildCommon.makeSpan(["mord", "accent"], [accentBody], options);
|
||||
|
||||
if (supsubGroup) {
|
||||
if (supSubGroup) {
|
||||
// Here, we replace the "base" child of the supsub with our newly
|
||||
// generated accent.
|
||||
supsubGroup.children[0] = accentWrap;
|
||||
supSubGroup.children[0] = accentWrap;
|
||||
|
||||
// Since we don't rerun the height calculation after replacing the
|
||||
// accent, we manually recalculate height.
|
||||
supsubGroup.height = Math.max(accentWrap.height, supsubGroup.height);
|
||||
supSubGroup.height = Math.max(accentWrap.height, supSubGroup.height);
|
||||
|
||||
// Accents should always be ords, even when their innards are not.
|
||||
supsubGroup.classes[0] = "mord";
|
||||
supSubGroup.classes[0] = "mord";
|
||||
|
||||
return supsubGroup;
|
||||
return supSubGroup;
|
||||
} else {
|
||||
return accentWrap;
|
||||
}
|
||||
};
|
||||
|
||||
const mathmlBuilder = (group, options) => {
|
||||
const mathmlBuilder: MathMLBuilder<"accent"> = (group, options) => {
|
||||
const groupValue = group.value;
|
||||
let accentNode;
|
||||
if (groupValue.isStretchy) {
|
||||
|
@@ -10,10 +10,11 @@ import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
|
||||
import type ParseNode from "../ParseNode";
|
||||
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
|
||||
|
||||
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "horizBrace", but
|
||||
// also "supsub" since an over/underbrace can affect super/subscripting.
|
||||
function htmlBuilder(grp: ParseNode<*>, options) {
|
||||
export const htmlBuilder: HtmlBuilderSupSub<"horizBrace"> = (grp, options) => {
|
||||
const style = options.style;
|
||||
|
||||
// Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node.
|
||||
@@ -29,11 +30,7 @@ function htmlBuilder(grp: ParseNode<*>, options) {
|
||||
supSub.value.sup, options.havingStyle(style.sup()), options) :
|
||||
html.buildGroup(
|
||||
supSub.value.sub, options.havingStyle(style.sub()), options);
|
||||
// The supsub `base` must be non-null in this context. Otherwise,
|
||||
// this `htmlBuilder` handler wouldn't have been invoked.
|
||||
// $FlowFixMe
|
||||
const base: ParseNode<*> = supSub.value.base;
|
||||
group = assertNodeType(base, "horizBrace");
|
||||
group = assertNodeType(supSub.value.base, "horizBrace");
|
||||
} else {
|
||||
group = assertNodeType(grp, "horizBrace");
|
||||
}
|
||||
@@ -112,7 +109,15 @@ function htmlBuilder(grp: ParseNode<*>, options) {
|
||||
|
||||
return buildCommon.makeSpan(
|
||||
["mord", (group.value.isOver ? "mover" : "munder")], [vlist], options);
|
||||
}
|
||||
};
|
||||
|
||||
const mathmlBuilder: MathMLBuilder<"horizBrace"> = (group, options) => {
|
||||
const accentNode = stretchy.mathMLnode(group.value.label);
|
||||
return new mathMLTree.MathNode(
|
||||
(group.value.isOver ? "mover" : "munder"),
|
||||
[mml.buildGroup(group.value.base, options), accentNode]
|
||||
);
|
||||
};
|
||||
|
||||
// Horizontal stretchy braces
|
||||
defineFunction({
|
||||
@@ -130,13 +135,5 @@ defineFunction({
|
||||
};
|
||||
},
|
||||
htmlBuilder,
|
||||
mathmlBuilder(group, options) {
|
||||
const accentNode = stretchy.mathMLnode(group.value.label);
|
||||
return new mathMLTree.MathNode(
|
||||
(group.value.isOver ? "mover" : "munder"),
|
||||
[mml.buildGroup(group.value.base, options), accentNode]
|
||||
);
|
||||
},
|
||||
mathmlBuilder,
|
||||
});
|
||||
|
||||
|
||||
|
@@ -6,23 +6,33 @@ import domTree from "../domTree";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
import utils from "../utils";
|
||||
import Style from "../Style";
|
||||
import {assertNodeType, checkNodeType} from "../ParseNode";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
|
||||
const htmlBuilder = (group, options) => {
|
||||
import type ParseNode from "../ParseNode";
|
||||
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
|
||||
|
||||
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "op", but also
|
||||
// "supsub" since some of them (like \int) can affect super/subscripting.
|
||||
export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
||||
// Operators are handled in the TeXbook pg. 443-444, rule 13(a).
|
||||
let supGroup;
|
||||
let subGroup;
|
||||
let hasLimits = false;
|
||||
if (group.type === "supsub") {
|
||||
let group: ParseNode<"op">;
|
||||
const supSub = checkNodeType(grp, "supsub");
|
||||
if (supSub) {
|
||||
// If we have limits, supsub will pass us its group to handle. Pull
|
||||
// out the superscript and subscript and set the group to the op in
|
||||
// its base.
|
||||
supGroup = group.value.sup;
|
||||
subGroup = group.value.sub;
|
||||
group = group.value.base;
|
||||
supGroup = supSub.value.sup;
|
||||
subGroup = supSub.value.sub;
|
||||
group = assertNodeType(supSub.value.base, "op");
|
||||
hasLimits = true;
|
||||
} else {
|
||||
group = assertNodeType(grp, "op");
|
||||
}
|
||||
|
||||
const style = options.style;
|
||||
@@ -190,7 +200,7 @@ const htmlBuilder = (group, options) => {
|
||||
}
|
||||
};
|
||||
|
||||
const mathmlBuilder = (group, options) => {
|
||||
const mathmlBuilder: MathMLBuilder<"op"> = (group, options) => {
|
||||
let node;
|
||||
|
||||
// TODO(emily): handle big operators using the `largeop` attribute
|
||||
|
Reference in New Issue
Block a user