Skip to content

Commit

Permalink
Fix KaTeX#4 - Spacing interactions between atoms
Browse files Browse the repository at this point in the history
  • Loading branch information
cbreeden committed Jun 4, 2016
1 parent 1cf10c4 commit af2ab4b
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 26 deletions.
74 changes: 49 additions & 25 deletions src/buildHTML.js
Expand Up @@ -22,11 +22,19 @@ var makeSpan = buildCommon.makeSpan;
* nodes. This function handles the `prev` node correctly, and passes the
* previous element from the list as the prev of the next element.
*/
var buildExpression = function(expression, options, prev) {
var buildExpression = function(expression, options, prev, next) {
var groups = [];
var localnext;
for (var i = 0; i < expression.length; i++) {
var group = expression[i];
groups.push(buildGroup(group, options, prev));
// While in a subtree, next should be the next sibling
// If at the last node, next is the parent's next sibling.
if (i + 1 < expression.length) {
localnext = expression[i+1];
} else {
localnext = next;
}
groups.push(buildGroup(group, options, prev, localnext));
prev = group;
}
return groups;
Expand Down Expand Up @@ -181,23 +189,37 @@ groupTypes.textord = function(group, options, prev) {
return buildCommon.makeOrd(group, options, "textord");
};

groupTypes.bin = function(group, options, prev) {
groupTypes.bin = function(group, options, prev, next) {
var className = "mbin";
// Pull out the most recent element. Do some special handling to find
// things at the end of a \color group. Note that we don't use the same
// logic for ordgroups (which count as ords).
var prevAtom = prev;
while (prevAtom && prevAtom.type === "color") {
var atoms = prevAtom.value.value;
while (prevAtom &&
(prevAtom.type === "color" || prevAtom.type === "phantom")) {
atoms = prevAtom.value.value;
prevAtom = atoms[atoms.length - 1];
}
// See TeXbook pg. 442-446, Rules 5 and 6, and the text before Rule 19.
// Here, we determine whether the bin should turn into an ord. We
// currently only apply Rule 5.
// Here, we determine whether the bin should turn into an ord.
if (!prev || utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
getTypeOfGroup(prevAtom))) {
group.type = "textord";
className = "mord";
} else {
// We only check the next atom if we didn't do anything with the
// previous atom.
var nextAtom = next;
while (nextAtom &&
(nextAtom.type === "color" || nextAtom.type === "phantom")) {
nextAtom = nextAtom.value.value[0];
}

if (!nextAtom || utils.contains(["mrel", "mclose", "mpunct"],
getTypeOfGroup(nextAtom))) {
group.type = "textord";
className = "mord";
}
}

return buildCommon.mathsym(
Expand Down Expand Up @@ -229,23 +251,24 @@ groupTypes.punct = function(group, options, prev) {
group.value, group.mode, options.getColor(), ["mpunct"]);
};

groupTypes.ordgroup = function(group, options, prev) {
groupTypes.ordgroup = function(group, options, prev, next) {
return makeSpan(
["mord", options.style.cls()],
buildExpression(group.value, options.reset())
buildExpression(group.value, options.reset(), prev, next)
);
};

groupTypes.text = function(group, options, prev) {
groupTypes.text = function(group, options, prev, next) {
return makeSpan(["text", "mord", options.style.cls()],
buildExpression(group.value.body, options.reset()));
buildExpression(group.value.body, options.reset(), prev, next));
};

groupTypes.color = function(group, options, prev) {
groupTypes.color = function(group, options, prev, next) {
var elements = buildExpression(
group.value.value,
options.withColor(group.value.color),
prev
prev,
next
);

// \color isn't supposed to affect the type of the elements it contains.
Expand Down Expand Up @@ -1054,12 +1077,12 @@ groupTypes.sqrt = function(group, options, prev) {
}
};

groupTypes.sizing = function(group, options, prev) {
groupTypes.sizing = function(group, options, prev, next) {
// Handle sizing operators like \Huge. Real TeX doesn't actually allow
// these functions inside of math expressions, so we do some special
// handling.
var inner = buildExpression(group.value.value,
options.withSize(group.value.size), prev);
options.withSize(group.value.size), prev, next);

var span = makeSpan(["mord"],
[makeSpan(["sizing", "reset-" + options.size, group.value.size,
Expand All @@ -1073,7 +1096,7 @@ groupTypes.sizing = function(group, options, prev) {
return span;
};

groupTypes.styling = function(group, options, prev) {
groupTypes.styling = function(group, options, prev, next) {
// Style changes are handled in the TeXbook on pg. 442, Rule 3.

// Figure out what style we're changing to.
Expand All @@ -1088,14 +1111,14 @@ groupTypes.styling = function(group, options, prev) {

// Build the inner expression in the new style.
var inner = buildExpression(
group.value.value, options.withStyle(newStyle), prev);
group.value.value, options.withStyle(newStyle), prev, next);

return makeSpan([options.style.reset(), newStyle.cls()], inner);
};

groupTypes.font = function(group, options, prev) {
groupTypes.font = function(group, options, prev, next) {
var font = group.value.font;
return buildGroup(group.value.body, options.withFont(font), prev);
return buildGroup(group.value.body, options.withFont(font), prev, next);
};

groupTypes.delimsizing = function(group, options, prev) {
Expand Down Expand Up @@ -1224,7 +1247,7 @@ groupTypes.kern = function(group, options, prev) {
return rule;
};

groupTypes.accent = function(group, options, prev) {
groupTypes.accent = function(group, options, prev, next) {
// Accents are handled in the TeXbook pg. 443, rule 12.
var base = group.value.base;

Expand All @@ -1250,7 +1273,7 @@ groupTypes.accent = function(group, options, prev) {
// Rerender the supsub group with its new base, and store that
// result.
supsubGroup = buildGroup(
supsub, options.reset(), prev);
supsub, options.reset(), prev, next);
}

// Build the base group
Expand Down Expand Up @@ -1330,11 +1353,12 @@ groupTypes.accent = function(group, options, prev) {
}
};

groupTypes.phantom = function(group, options, prev) {
groupTypes.phantom = function(group, options, prev, next) {
var elements = buildExpression(
group.value.value,
options.withPhantom(),
prev
prev,
next
);

// \phantom isn't supposed to affect the elements it contains.
Expand All @@ -1347,14 +1371,14 @@ groupTypes.phantom = function(group, options, prev) {
* function for it. It also handles the interaction of size and style changes
* between parents and children.
*/
var buildGroup = function(group, options, prev) {
var buildGroup = function(group, options, prev, next) {
if (!group) {
return makeSpan();
}

if (groupTypes[group.type]) {
// Call the groupTypes function
var groupNode = groupTypes[group.type](group, options, prev);
var groupNode = groupTypes[group.type](group, options, prev, next);
var multiplier;

// If the style changed between the parent and the current group,
Expand Down
24 changes: 23 additions & 1 deletion test/katex-spec.js
Expand Up @@ -1488,19 +1488,41 @@ describe("A bin builder", function() {
expect(built[0].classes).toContain("mord");
expect(built[0].classes).not.toContain("mbin");
});

it("should create ords when at the end of a list", function() {
var built = getBuilt("1\times");

expect(built[1].classes).toContain("mord");
expect(built[1].classes).not.toContain("mbin");
});

it("should create ords before some other objects", function() {
expect(getBuilt("x + < 2")[1].classes).toContain("mord");
expect(getBuilt("x \times .")[1].classes).toContain("mord");
expect(getBuilt("x + )")[1].classes).toContain("mord");
});

it("should create ords after some other objects", function() {
expect(getBuilt("x + + 2")[2].classes).toContain("mord");
expect(getBuilt("x + + 2")[1].classes).toContain("mbin");
expect(getBuilt("( + 2")[1].classes).toContain("mord");
expect(getBuilt("= + 2")[1].classes).toContain("mord");
expect(getBuilt("\\sin + 2")[1].classes).toContain("mord");
expect(getBuilt(", + 2")[1].classes).toContain("mord");
});

it("should correctly interact with color objects", function() {
expect(getBuilt("\\blue{x}+y")[1].classes).toContain("mbin");
expect(getBuilt("\\blue{x+}+y")[1].classes).toContain("mord");
});

it("should correctly interact with phantom objects", function() {
expect(getBuilt("\\phantom{x}+y")[1].classes).toContain("mbin");
expect(getBuilt("\\phantom{x+}+y")[1].classes).toContain("mord");
expect(getBuilt("x + \\phantom{y}")[1].classes).toContain("mbin");
expect(getBuilt("x + \\phantom{+ y}")[2]
.children[0].classes).toContain("mord");
})
});

describe("A markup generator", function() {
Expand Down

0 comments on commit af2ab4b

Please sign in to comment.