-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Fix space handling #912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix space handling #912
Changes from 2 commits
ba59a82
33e7ef7
5af2fe4
5587386
f479f1c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,9 +140,13 @@ export default class Parser { | |
| * and fetches the one after that as the new look ahead. | ||
| */ | ||
| consume() { | ||
| this.nextToken = this.gullet.get(this.mode === "math"); | ||
| this.nextToken = this.gullet.get(false); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is the only place where
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I wanted a second opinion on this. As I wrote above: "we never use "It also means that
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you think
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we still need |
||
| } | ||
|
|
||
| /** | ||
| * Switches between "text" and "math" modes, reconsuming nextToken | ||
| * in case it would be read differently in the new mode. | ||
| */ | ||
| switchMode(newMode) { | ||
| this.gullet.unget(this.nextToken); | ||
| this.mode = newMode; | ||
|
|
@@ -193,6 +197,10 @@ export default class Parser { | |
| // Keep adding atoms to the body until we can't parse any more atoms (either | ||
| // we reached the end, a }, or a \right) | ||
| while (true) { | ||
| // Ignore spaces in math mode | ||
| if (this.mode === "math") { | ||
| this.consumeSpaces(); | ||
| } | ||
| const lex = this.nextToken; | ||
| if (Parser.endOfExpression.indexOf(lex.text) !== -1) { | ||
| break; | ||
|
|
@@ -283,6 +291,7 @@ export default class Parser { | |
| const symbolToken = this.nextToken; | ||
| const symbol = symbolToken.text; | ||
| this.consume(); | ||
| this.consumeSpaces(); // ignore spaces before sup/subscript argument | ||
| const group = this.parseGroup(); | ||
|
|
||
| if (!group) { | ||
|
|
@@ -366,6 +375,9 @@ export default class Parser { | |
| let superscript; | ||
| let subscript; | ||
| while (true) { | ||
| // Guaranteed in math mode, so eat any spaces first. | ||
| this.consumeSpaces(); | ||
|
|
||
| // Lex the first token | ||
| const lex = this.nextToken; | ||
|
|
||
|
|
@@ -674,9 +686,16 @@ export default class Parser { | |
| const optArgs = []; | ||
|
|
||
| for (let i = 0; i < totalArgs; i++) { | ||
| const nextToken = this.nextToken; | ||
| const argType = funcData.argTypes && funcData.argTypes[i]; | ||
| const isOptional = i < funcData.numOptionalArgs; | ||
| // Ignore spaces between arguments. As the TeXbook says: | ||
| // "After you have said ‘\def\row#1#2{...}’, you are allowed to | ||
| // put spaces between the arguments (e.g., ‘\row x n’), because | ||
| // TeX doesn’t use single spaces as undelimited arguments." | ||
| if (i > 0 && !isOptional) { | ||
| this.consumeSpaces(); | ||
| } | ||
| const nextToken = this.nextToken; | ||
| let arg = argType ? | ||
| this.parseGroupOfType(argType, isOptional) : | ||
| this.parseGroup(isOptional); | ||
|
|
@@ -733,14 +752,9 @@ export default class Parser { | |
| return this.parseSizeGroup(optional); | ||
| } | ||
|
|
||
| this.switchMode(innerMode); | ||
| if (innerMode === "text") { | ||
| // text mode is special because it should ignore the whitespace before | ||
| // it | ||
| this.consumeSpaces(); | ||
| } | ||
| // By the time we get here, innerMode is one of "text" or "math". | ||
| // We switch the mode of the parser, recurse, then restore the old mode. | ||
| this.switchMode(innerMode); | ||
| const res = this.parseGroup(optional); | ||
| this.switchMode(outerMode); | ||
| return res; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -188,9 +188,11 @@ describe("A parser", function() { | |
| }); | ||
|
|
||
| it("should ignore whitespace", function() { | ||
| const parseA = stripPositions(getParsed(" x y ")); | ||
| const parseB = stripPositions(getParsed("xy")); | ||
| expect(parseA).toEqual(parseB); | ||
| expect(" x y ").toParseLike("xy"); | ||
| }); | ||
|
|
||
| it("should ignore whitespace in atom", function() { | ||
| expect(" x ^ y ").toParseLike("x^y"); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -2353,6 +2355,11 @@ describe("An aligned environment", function() { | |
| .toParse(); | ||
| }); | ||
|
|
||
| it("should allow cells in brackets", function() { | ||
| expect("\\begin{aligned}[a]&[b]\\\\ [c]&[d]\\end{aligned}") | ||
| .toParse(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And if there's no space between
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. This matches LaTeX behavior, based on testing. ( |
||
| }); | ||
|
|
||
| }); | ||
|
|
||
| describe("A parser that does not throw on unsupported commands", function() { | ||
|
|
@@ -2397,7 +2404,7 @@ describe("A parser that does not throw on unsupported commands", function() { | |
| }); | ||
| }); | ||
|
|
||
| describe("The symbol table integraty", function() { | ||
| describe("The symbol table integrity", function() { | ||
| it("should treat certain symbols as synonyms", function() { | ||
| expect(getBuilt("<")).toEqual(getBuilt("\\lt")); | ||
| expect(getBuilt(">")).toEqual(getBuilt("\\gt")); | ||
|
|
@@ -2431,10 +2438,30 @@ describe("A macro expander", function() { | |
| compareParseTree("\\foo", "x", {"\\foo": " x"}); | ||
| }); | ||
|
|
||
| it("should consume spaces after macro", function() { | ||
| it("should consume spaces after control-word macro", function() { | ||
| compareParseTree("\\text{\\foo }", "\\text{x}", {"\\foo": "x"}); | ||
| }); | ||
|
|
||
| it("should consume spaces after macro with \\relax", function() { | ||
| compareParseTree("\\text{\\foo }", "\\text{}", {"\\foo": "\\relax"}); | ||
| }); | ||
|
|
||
| it("should consume spaces after \\relax", function() { | ||
| compareParseTree("\\text{\\relax }", "\\text{}"); | ||
| }); | ||
|
|
||
| it("should consume spaces after control-word function", function() { | ||
| compareParseTree("\\text{\\KaTeX x}", "\\text{\\KaTeX\\relax x}"); | ||
|
||
| }); | ||
|
|
||
| it("should preserve spaces after control-symbol macro", function() { | ||
| compareParseTree("\\text{\\% y}", "\\text{x y}", {"\\%": "x"}); | ||
| }); | ||
|
|
||
| it("should preserve spaces after control-symbol function", function() { | ||
| expect("\\text{\\' }").toParse(); | ||
| }); | ||
|
|
||
| it("should consume spaces between arguments", function() { | ||
| compareParseTree("\\text{\\foo 1 2}", "\\text{12end}", {"\\foo": "#1#2end"}); | ||
| compareParseTree("\\text{\\foo {1} {2}}", "\\text{12end}", {"\\foo": "#1#2end"}); | ||
|
|
@@ -2475,13 +2502,37 @@ describe("A macro expander", function() { | |
| }); | ||
| }); | ||
|
|
||
| it("should allow for space second argument (text version)", function() { | ||
| compareParseTree("\\text{\\foo\\bar\\bar}", "\\text{( , )}", { | ||
| "\\foo": "(#1,#2)", | ||
| "\\bar": " ", | ||
| }); | ||
| }); | ||
|
|
||
| it("should allow for space second argument (math version)", function() { | ||
| compareParseTree("\\foo\\bar\\bar", "(,)", { | ||
| "\\foo": "(#1,#2)", | ||
| "\\bar": " ", | ||
| }); | ||
| }); | ||
|
|
||
| it("should allow for empty macro argument", function() { | ||
| compareParseTree("\\foo\\bar", "()", { | ||
| "\\foo": "(#1)", | ||
| "\\bar": "", | ||
| }); | ||
| }); | ||
|
|
||
| // The following is not currently possible to get working, given that | ||
| // functions and macros are dealt with separately. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the current behavior? Can you open an issue for this?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Opened #924. Also added some more comments in the code about this. |
||
| /* | ||
| it("should allow for space function arguments", function() { | ||
| compareParseTree("\\frac\\bar\\bar", "\\frac{}{}", { | ||
| "\\bar": " ", | ||
| }); | ||
| }); | ||
| */ | ||
|
|
||
| it("should expand the \\overset macro as expected", function() { | ||
| expect("\\overset?=").toParseLike("\\mathop{=}\\limits^{?}"); | ||
| expect("\\overset{x=y}{\\sqrt{ab}}") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice comment