From e449b2d61ac985739d620538225cec0d674e9dc2 Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Thu, 8 Dec 2016 11:28:48 -0500 Subject: [PATCH] Handle \middle. --- src/Parser.js | 4 ++ src/buildHTML.js | 34 +++++++++- src/buildMathML.js | 7 ++ src/functions.js | 60 ++++++++++++------ test/katex-spec.js | 20 ++++++ .../images/LeftRightMiddle-chrome.png | Bin 0 -> 11285 bytes .../images/LeftRightMiddle-firefox.png | Bin 0 -> 10656 bytes test/screenshotter/ss_data.yaml | 1 + 8 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 test/screenshotter/images/LeftRightMiddle-chrome.png create mode 100644 test/screenshotter/images/LeftRightMiddle-firefox.png diff --git a/src/Parser.js b/src/Parser.js index 97bbbf3f..0b87bc8e 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -52,6 +52,8 @@ function Parser(input, settings) { this.gullet = new MacroExpander(input, settings.macros); // Store the settings for use in parsing this.settings = settings; + // Count leftright depth (for \middle errors) + this.leftrightDepth = 0; } var ParseNode = parseData.ParseNode; @@ -411,7 +413,9 @@ Parser.prototype.parseImplicitGroup = function() { // Parse the entire left function (including the delimiter) var left = this.parseFunction(start); // Parse out the implicit body + ++this.leftrightDepth; body = this.parseExpression(false); + --this.leftrightDepth; // Check the next token this.expect("\\right", false); var right = this.parseFunction(); diff --git a/src/buildHTML.js b/src/buildHTML.js index dccf161a..dcf71107 100644 --- a/src/buildHTML.js +++ b/src/buildHTML.js @@ -1186,11 +1186,16 @@ groupTypes.leftright = function(group, options) { var innerHeight = 0; var innerDepth = 0; + var hadMiddle = false; // Calculate its height and depth for (var i = 0; i < inner.length; i++) { - innerHeight = Math.max(inner[i].height, innerHeight); - innerDepth = Math.max(inner[i].depth, innerDepth); + if (inner[i].isMiddle) { + hadMiddle = true; + } else { + innerHeight = Math.max(inner[i].height, innerHeight); + innerDepth = Math.max(inner[i].depth, innerDepth); + } } var style = options.style; @@ -1215,6 +1220,18 @@ groupTypes.leftright = function(group, options) { // Add it to the beginning of the expression inner.unshift(leftDelim); + // Handle middle delimiters + if (hadMiddle) { + for (i = 1; i < inner.length; i++) { + if (inner[i].isMiddle) { + // Apply the options that were active when \middle was called + inner[i] = delimiter.leftRightDelim( + inner[i].isMiddle.value, innerHeight, innerDepth, + inner[i].isMiddle.options, group.mode, []); + } + } + } + var rightDelim; // Same for the right delimiter if (group.value.right === ".") { @@ -1231,6 +1248,19 @@ groupTypes.leftright = function(group, options) { ["minner", style.cls()], inner, options); }; +groupTypes.middle = function(group, options) { + var middleDelim; + if (group.value.value === ".") { + middleDelim = makeNullDelimiter(options, []); + } else { + middleDelim = delimiter.sizedDelim( + group.value.value, 1, options, + group.mode, []); + middleDelim.isMiddle = {value: group.value.value, options: options}; + } + return middleDelim; +}; + groupTypes.rule = function(group, options) { // Make an empty span for the rule var rule = makeSpan(["mord", "rule"], [], options); diff --git a/src/buildMathML.js b/src/buildMathML.js index 11a088f9..795f402c 100644 --- a/src/buildMathML.js +++ b/src/buildMathML.js @@ -285,6 +285,13 @@ groupTypes.leftright = function(group, options) { return outerNode; }; +groupTypes.middle = function(group, options) { + var middleNode = new mathMLTree.MathNode( + "mo", [makeText(group.value.middle, group.mode)]); + middleNode.setAttribute("fence", "true"); + return middleNode; +}; + groupTypes.accent = function(group, options) { var accentNode = new mathMLTree.MathNode( "mo", [makeText(group.value.accent, group.mode)]); diff --git a/src/functions.js b/src/functions.js index 6966ea7f..121bc995 100644 --- a/src/functions.js +++ b/src/functions.js @@ -496,37 +496,61 @@ defineFunction(["\\llap", "\\rlap"], { }); // Delimiter functions +var checkDelimiter = function(delim, context) { + if (utils.contains(delimiters, delim.value)) { + return delim; + } else { + throw new ParseError( + "Invalid delimiter: '" + delim.value + "' after '" + + context.funcName + "'", delim); + } +}; + defineFunction([ "\\bigl", "\\Bigl", "\\biggl", "\\Biggl", "\\bigr", "\\Bigr", "\\biggr", "\\Biggr", "\\bigm", "\\Bigm", "\\biggm", "\\Biggm", "\\big", "\\Big", "\\bigg", "\\Bigg", +], { + numArgs: 1, +}, function(context, args) { + var delim = checkDelimiter(args[0], context); + + return { + type: "delimsizing", + size: delimiterSizes[context.funcName].size, + mclass: delimiterSizes[context.funcName].mclass, + value: delim.value, + }; +}); + +defineFunction([ "\\left", "\\right", ], { numArgs: 1, }, function(context, args) { - var delim = args[0]; - if (!utils.contains(delimiters, delim.value)) { - throw new ParseError( - "Invalid delimiter: '" + delim.value + "' after '" + - context.funcName + "'", delim); - } + var delim = checkDelimiter(args[0], context); // \left and \right are caught somewhere in Parser.js, which is // why this data doesn't match what is in buildHTML. - if (context.funcName === "\\left" || context.funcName === "\\right") { - return { - type: "leftright", - value: delim.value, - }; - } else { - return { - type: "delimsizing", - size: delimiterSizes[context.funcName].size, - mclass: delimiterSizes[context.funcName].mclass, - value: delim.value, - }; + return { + type: "leftright", + value: delim.value, + }; +}); + +defineFunction("\\middle", { + numArgs: 1, +}, function(context, args) { + var delim = checkDelimiter(args[0], context); + if (!context.parser.leftrightDepth) { + throw new ParseError("\\middle without preceding \\left", delim); } + + return { + type: "middle", + value: delim.value, + }; }); // Sizing functions (handled in Parser.js explicitly, hence no handler) diff --git a/test/katex-spec.js b/test/katex-spec.js index 2018b916..21617cc6 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -1042,6 +1042,26 @@ describe("A left/right parser", function() { var normalEmpty = "\\Bigl ."; expect(normalEmpty).toParse(); }); + + it("should handle \\middle", function() { + var normalMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\right)"; + expect(normalMiddle).toParse(); + }); + + it("should handle multiple \\middles", function() { + var multiMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\middle/ \\dfrac{z}{q} \\right)"; + expect(multiMiddle).toParse(); + }); + + it("should handle nested \\middles", function() { + var nestedMiddle = "\\left( a^2 \\middle| \\left( b \\middle/ c \\right) \\right)"; + expect(nestedMiddle).toParse(); + }); + + it("should error when \\middle is not in \\left...\\right", function() { + var unmatchedMiddle = "(\\middle|\\dfrac{x}{y})"; + expect(unmatchedMiddle).toNotParse(); + }); }); describe("A begin/end parser", function() { diff --git a/test/screenshotter/images/LeftRightMiddle-chrome.png b/test/screenshotter/images/LeftRightMiddle-chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..f731f146e2451a31400161d352859500b66d7049 GIT binary patch literal 11285 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 literal 0 HcmV?d00001 diff --git a/test/screenshotter/images/LeftRightMiddle-firefox.png b/test/screenshotter/images/LeftRightMiddle-firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..4fe9d8c6b8cb6dd24bf34915fcac007cdbdb0398 GIT binary patch literal 10656 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 I=akR{0Qo&U2mk;8 literal 0 HcmV?d00001 diff --git a/test/screenshotter/ss_data.yaml b/test/screenshotter/ss_data.yaml index a37a6920..4d0991ec 100644 --- a/test/screenshotter/ss_data.yaml +++ b/test/screenshotter/ss_data.yaml @@ -72,6 +72,7 @@ Kern: Lap: ab\llap{f}cd\rlap{g}h LeftRight: \left( x^2 \right) \left\{ x^{x^{x^{x^x}}} \right. LeftRightListStyling: a+\left(x+y\right)-x +LeftRightMiddle: \left( x^2 \middle/ \right) \left\{ x^{x^{x^{x^x}}} \middle/ y \right. LeftRightStyleSizing: | +\left\{\rule{0.1em}{1em}\right. x^{+\left\{\rule{0.1em}{1em}\right.