commit 0be77d4e6ff7b3752c609e2cb161f82fd230d67b
parent a76f6eb985659f450960e3b5b22aa17650f26e41
Author: Emily Eisenberg <emily@khanacademy.org>
Date: Wed, 10 Sep 2014 17:10:38 -0700
Add style changing functions
Summary:
Add \displaystyle, \textstyle, \scriptstyle, and \scriptscriptstyle commands.
Added tests and huxley screenshots for everything that looks different in
displaystyle vs normal style.
Fixes #24.
Test Plan:
- See new tests work, and old tests still work
- See no huxley screenshots changed, and new screenshot looks good
Reviewers: alpert
Reviewed By: alpert
Differential Revision: http://phabricator.khanacademy.org/D13079
Diffstat:
7 files changed, 97 insertions(+), 5 deletions(-)
diff --git a/Parser.js b/Parser.js
@@ -242,6 +242,11 @@ var sizeFuncs = [
"\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge"
];
+// A list of the style-changing functions, for use in parseImplicitGroup
+var styleFuncs = [
+ "\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle"
+];
+
// Parses an implicit group, which is a group that starts at the end of a
// specified, and ends right before a higher explicit group ends, or at EOL. It
// is used for functions that appear to affect the current style, like \Large or
@@ -259,7 +264,9 @@ Parser.prototype.parseImplicitGroup = function(pos, mode) {
return this.parseFunction(pos, mode);
}
- if (start.result.result === "\\left") {
+ var func = start.result.result;
+
+ if (func === "\\left") {
// If we see a left:
// Parse the entire left function (including the delimiter)
var left = this.parseFunction(pos, mode);
@@ -282,17 +289,28 @@ Parser.prototype.parseImplicitGroup = function(pos, mode) {
} else {
throw new ParseError("Missing \\right", this.lexer, body.position);
}
- } else if (start.result.result === "\\right") {
+ } else if (func === "\\right") {
// If we see a right, explicitly fail the parsing here so the \left
// handling ends the group
return null;
- } else if (utils.contains(sizeFuncs, start.result.result)) {
+ } else if (utils.contains(sizeFuncs, func)) {
// If we see a sizing function, parse out the implict body
var body = this.handleExpressionBody(start.result.position, mode);
return new ParseResult(
new ParseNode("sizing", {
// Figure out what size to use based on the list of functions above
- size: "size" + (utils.indexOf(sizeFuncs, start.result.result) + 1),
+ size: "size" + (utils.indexOf(sizeFuncs, func) + 1),
+ value: body.body
+ }, mode),
+ body.position);
+ } else if (utils.contains(styleFuncs, func)) {
+ // If we see a styling function, parse out the implict body
+ var body = this.handleExpressionBody(start.result.position, mode);
+ return new ParseResult(
+ new ParseNode("styling", {
+ // Figure out what style to use by pulling out the style from
+ // the function name
+ style: func.slice(1, func.length - 5),
value: body.body
}, mode),
body.position);
diff --git a/buildTree.js b/buildTree.js
@@ -54,6 +54,8 @@ var getTypeOfGroup = function(group) {
return getTypeOfGroup(group.value.value);
} else if (group.type === "sizing") {
return getTypeOfGroup(group.value.value);
+ } else if (group.type === "styling") {
+ return getTypeOfGroup(group.value.value);
} else if (group.type === "delimsizing") {
return groupToType[group.value.delimType];
} else {
@@ -547,6 +549,22 @@ var groupTypes = {
return span;
},
+ styling: function(group, options, prev) {
+ var style = {
+ "display": Style.DISPLAY,
+ "text": Style.TEXT,
+ "script": Style.SCRIPT,
+ "scriptscript": Style.SCRIPTSCRIPT
+ };
+
+ var newStyle = style[group.value.style];
+
+ var inner = buildExpression(
+ group.value.value, options.withStyle(newStyle), prev);
+
+ return makeSpan([options.style.reset(), newStyle.cls()], inner);
+ },
+
delimsizing: function(group, options, prev) {
var delim = group.value.value;
diff --git a/functions.js b/functions.js
@@ -310,7 +310,7 @@ var duplicatedFunctions = [
}
},
- // Sizing functions
+ // Sizing functions (handled in Parser.js explicitly, hence no handler)
{
funcs: [
"\\tiny", "\\scriptsize", "\\footnotesize", "\\small",
@@ -319,6 +319,18 @@ var duplicatedFunctions = [
data: {
numArgs: 0
}
+ },
+
+ // Style changing functions (handled in Parser.js explicitly, hence no
+ // handler)
+ {
+ funcs: [
+ "\\displaystyle", "\\textstyle", "\\scriptstyle",
+ "\\scriptscriptstyle"
+ ],
+ data: {
+ numArgs: 0
+ }
}
];
diff --git a/test/huxley/DisplayStyle.hux/firefox-1.png b/test/huxley/DisplayStyle.hux/firefox-1.png
Binary files differ.
diff --git a/test/huxley/DisplayStyle.hux/record.json b/test/huxley/DisplayStyle.hux/record.json
@@ -0,0 +1,5 @@
+[
+ {
+ "action": "screenshot"
+ }
+]
diff --git a/test/huxley/Huxleyfile.json b/test/huxley/Huxleyfile.json
@@ -159,5 +159,11 @@
"name": "Sqrt",
"screenSize": [1024, 768],
"url": "http://localhost:7936/test/huxley/test.html?m=\\sqrt{\\sqrt{\\sqrt{x}}}_{\\sqrt{\\sqrt{x}}}^{\\sqrt{\\sqrt{\\sqrt{x}}}^{\\sqrt{\\sqrt{\\sqrt{x}}}}}"
+ },
+
+ {
+ "name": "DisplayStyle",
+ "screenSize": [1024, 768],
+ "url": "http://localhost:7936/test/huxley/test.html?m={\\displaystyle\\sqrt{x}}{\\sqrt{x}}{\\displaystyle \\frac12}{\\frac12}{\\displaystyle x^1_2}{x^1_2}"
}
]
diff --git a/test/katex-tests.js b/test/katex-tests.js
@@ -916,3 +916,36 @@ describe("A TeX-compliant parser", function() {
}
});
});
+
+describe("A style change parser", function() {
+ it("should not fail", function() {
+ expect("\\displaystyle x").toParse();
+ expect("\\textstyle x").toParse();
+ expect("\\scriptstyle x").toParse();
+ expect("\\scriptscriptstyle x").toParse();
+ });
+
+ it("should produce the correct style", function() {
+ var displayParse = parseTree("\\displaystyle x")[0];
+ expect(displayParse.value.style).toMatch("display");
+
+ var scriptscriptParse = parseTree("\\scriptscriptstyle x")[0];
+ expect(scriptscriptParse.value.style).toMatch("scriptscript");
+ });
+
+ it("should only change the style within its group", function() {
+ var text = "a b { c d \\displaystyle e f } g h";
+ expect(text).toParse();
+
+ var parse = parseTree(text);
+
+ var displayNode = parse[2].value[2];
+
+ expect(displayNode.type).toMatch("styling");
+
+ var displayBody = displayNode.value.value;
+
+ expect(displayBody.length).toEqual(2);
+ expect(displayBody[0].value).toMatch("e");
+ });
+});