From af2ab4b4233b04672af8c115c95425ac2193a7e1 Mon Sep 17 00:00:00 2001 From: Christopher Breeden Date: Fri, 3 Jun 2016 20:58:19 -0500 Subject: [PATCH] Fix #4 - Spacing interactions between atoms --- src/buildHTML.js | 74 ++++++++++++++++++++++++++++++---------------- test/katex-spec.js | 24 ++++++++++++++- 2 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/buildHTML.js b/src/buildHTML.js index cc6b30314a..cd0ef5e0a3 100644 --- a/src/buildHTML.js +++ b/src/buildHTML.js @@ -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; @@ -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( @@ -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. @@ -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, @@ -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. @@ -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) { @@ -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; @@ -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 @@ -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. @@ -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, diff --git a/test/katex-spec.js b/test/katex-spec.js index 07b7d7519c..4cdb59fb95 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -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() {