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:
Ashish Myles
2018-05-24 23:28:58 -04:00
committed by ylemkimon
parent a5e6b92ff1
commit 1e629a0310
5 changed files with 67 additions and 42 deletions

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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,
});

View File

@@ -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