Support \( and fix instant mode switching for $, \(, \text (#1213)

* Fix #1212 by supporting `\(...\)` in addition to `$...$` inline math nested inside `\text`. This turned out to be harder than one would think because of the way `$` was being handled specially, while `\(` needed to be handled as a function, which immediately consumed the `\(` token, which meant that the first following token got consumed in text mode instead of math mode (unlike `$` where we could switch the mode before consumption).
* Added new `consumeMode` setting for functions which tells the parser to switch modes just for the consumption of the command token.
* Now that we're working with functions, move all the `$` handling code into `functions/math.js`.  Somewhat bizarrely, we can define `$` as a function (nearly identical to `\(`) and it all works (just like we can have single-character macros).  This move removed a lot of messy stuff from `Parser.js`: `ParsedDollar`, `ParsedFuncOrArgOrDollar`, `newDollar`, `assertFuncOrArg`, and at least three explicit checks for `if (... === "$")`.
* Moved the `consume()` for the command token from `parseSymbol` to `parseGivenFunction`.  This ended up not being strictly necessary, but seems conceptually cleaner.
* Partially address #1027 by setting `consumeMode: "text"` for `\text` commands.  As a result, `\text` now instantly switches the mode to `"text"`, so even an unbraced macro argument will properly detect text mode.  I added a test for this, which is a slightly weaker form of #1027.
This commit is contained in:
Erik Demaine
2018-03-14 10:54:36 -04:00
committed by GitHub
parent 5bcdeec4ad
commit 4f29c5a942
7 changed files with 133 additions and 67 deletions

View File

@@ -812,7 +812,6 @@ describe("A text parser", function() {
const badTextExpression = "\\text{a b%}";
const badFunctionExpression = "\\text{\\sqrt{x}}";
const mathTokenAfterText = "\\text{sin}^2";
const textWithEmbeddedMath = "\\text{graph: $y = mx + b$}";
it("should not fail", function() {
expect(textExpression).toParse();
@@ -872,7 +871,40 @@ describe("A text parser", function() {
});
it("should parse math within text group", function() {
expect(textWithEmbeddedMath).toParse();
expect("\\text{graph: $y = mx + b$}").toParse();
expect("\\text{graph: \\(y = mx + b\\)}").toParse();
});
it("should parse math within text within math within text", function() {
expect("\\text{hello $x + \\text{world $y$} + z$}").toParse();
expect("\\text{hello \\(x + \\text{world $y$} + z\\)}").toParse();
expect("\\text{hello $x + \\text{world \\(y\\)} + z$}").toParse();
expect("\\text{hello \\(x + \\text{world \\(y\\)} + z\\)}").toParse();
});
it("should forbid \\( within math mode", function() {
expect("\\(").toNotParse();
expect("\\text{$\\(x\\)$}").toNotParse();
});
it("should forbid $ within math mode", function() {
expect("$x$").toNotParse();
expect("\\text{\\($x$\\)}").toNotParse();
});
it("should detect unbalanced \\)", function() {
expect("\\)").toNotParse();
expect("\\text{\\)}").toNotParse();
});
it("should detect unbalanced $", function() {
expect("$").toNotParse();
expect("\\text{$}").toNotParse();
});
it("should not mix $ and \\(..\\)", function() {
expect("\\text{$x\\)}").toNotParse();
expect("\\text{\\(x$}").toNotParse();
});
it("should parse spacing functions", function() {
@@ -2824,8 +2856,13 @@ describe("A macro expander", function() {
{"\\mode": "\\TextOrMath{text}{math}"});
});
// TODO(edemaine): This doesn't work yet. Parses like `\text math`,
// which doesn't even treat all four letters as an argument.
it("\\TextOrMath should work in a macro passed to \\text", function() {
compareParseTree("\\text\\mode", "\\text t",
{"\\mode": "\\TextOrMath{t}{m}"});
});
// TODO(edemaine): This doesn't work yet. Parses like `\text text`,
// which doesn't treat all four letters as an argument.
//it("\\TextOrMath should work in a macro passed to \\text", function() {
// compareParseTree("\\text\\mode", "\\text{text}",
// {"\\mode": "\\TextOrMath{text}{math}"});