Support \arraystretch as a macro definition (#1381)

* Support \arraystretch as a macro definition

Also add `expandMacro` and `expandMacroAsText` helpers to `MacroExpander`.

* Remove excess defaulting

* Add test
This commit is contained in:
Erik Demaine
2018-06-03 18:19:23 -04:00
committed by Kevin Barabash
parent 563b0d5f8f
commit fcb32f058b
5 changed files with 212 additions and 6 deletions

View File

@@ -249,6 +249,40 @@ export default class MacroExpander implements MacroContextInterface {
throw new Error(); // eslint-disable-line no-unreachable
}
/**
* Fully expand the given macro name and return the resulting list of
* tokens, or return `undefined` if no such macro is defined.
*/
expandMacro(name: string): Token[] | void {
if (!this.macros.get(name)) {
return undefined;
}
const output = [];
const oldStackLength = this.stack.length;
this.pushToken(new Token(name));
while (this.stack.length > oldStackLength) {
const expanded = this.expandOnce();
// expandOnce returns Token if and only if it's fully expanded.
if (expanded instanceof Token) {
output.push(this.stack.pop());
}
}
return output;
}
/**
* Fully expand the given macro name and return the result as a string,
* or return `undefined` if no such macro is defined.
*/
expandMacroAsText(name: string): string | void {
const tokens = this.expandMacro(name);
if (tokens) {
return tokens.map((token) => token.text).join("");
} else {
return tokens;
}
}
/**
* Returns the expanded macro as a reversed array of tokens and a macro
* argument count. Or returns `null` if no such macro.

View File

@@ -25,7 +25,7 @@ type AlignSpec = { type: "separator", separator: string } | {
export type ArrayEnvNodeData = {|
type: "array",
hskipBeforeAndAfter?: boolean,
arraystretch?: number,
arraystretch: number,
addJot?: boolean,
cols?: AlignSpec[],
body: ParseNode<*>[][], // List of rows in the (2D) array.
@@ -71,6 +71,20 @@ function parseArray(
parser.gullet.beginGroup();
parser.gullet.macros.set("\\\\", "\\cr");
// Get current arraystretch if it's not set by the environment
if (!result.arraystretch) {
const arraystretch = parser.gullet.expandMacroAsText("\\arraystretch");
if (arraystretch == null) {
// Default \arraystretch from lttab.dtx
result.arraystretch = 1;
} else {
result.arraystretch = parseFloat(arraystretch);
if (!result.arraystretch || result.arraystretch < 0) {
throw new ParseError(`Invalid \\arraystretch: ${arraystretch}`);
}
}
}
let row = [];
const body = [row];
const rowGaps = [];
@@ -166,10 +180,7 @@ const htmlBuilder = function(group, options) {
// Default \jot from ltmath.dtx
// TODO(edemaine): allow overriding \jot via \setlength (#687)
const jot = 3 * pt;
// Default \arraystretch from lttab.dtx
// TODO(gagern): may get redefined once we have user-defined macros
const arraystretch = utils.deflt(groupValue.arraystretch, 1);
const arrayskip = arraystretch * baselineskip;
const arrayskip = groupValue.arraystretch * baselineskip;
const arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and
const arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx
@@ -358,7 +369,7 @@ const mathmlBuilder = function(group, options) {
}));
};
// Convinient function for aligned and alignedat environments.
// Convenience function for aligned and alignedat environments.
const alignedHandler = function(context, args) {
const cols = [];
let res = {

View File

@@ -38,6 +38,18 @@ export interface MacroContextInterface {
*/
expandAfterFuture(): Token;
/**
* Fully expand the given macro name and return the resulting list of
* tokens, or return `undefined` if no such macro is defined.
*/
expandMacro(name: string): Token[] | void;
/**
* Fully expand the given macro name and return the result as a string,
* or return `undefined` if no such macro is defined.
*/
expandMacroAsText(name: string): string | void;
/**
* Consume the specified number of arguments from the token stream,
* and return the resulting array of arguments.

View File

@@ -1,5 +1,148 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`A begin/end parser should grab \\arraystretch 1`] = `
[
{
"type": "array",
"mode": "math",
"value": {
"type": "array",
"arraystretch": 1.5,
"body": [
[
{
"type": "styling",
"mode": "math",
"value": {
"type": "styling",
"style": "text",
"value": [
{
"type": "ordgroup",
"mode": "math",
"value": [
{
"type": "mathord",
"loc": {
"end": 37,
"lexer": {
"input": "\\\\def\\\\arraystretch{1.5}\\\\begin{matrix}a&b\\\\\\\\c&d\\\\end{matrix}",
"pos": 56
},
"start": 36
},
"mode": "math",
"value": "a"
}
]
}
]
}
},
{
"type": "styling",
"mode": "math",
"value": {
"type": "styling",
"style": "text",
"value": [
{
"type": "ordgroup",
"mode": "math",
"value": [
{
"type": "mathord",
"loc": {
"end": 39,
"lexer": {
"input": "\\\\def\\\\arraystretch{1.5}\\\\begin{matrix}a&b\\\\\\\\c&d\\\\end{matrix}",
"pos": 56
},
"start": 38
},
"mode": "math",
"value": "b"
}
]
}
]
}
}
],
[
{
"type": "styling",
"mode": "math",
"value": {
"type": "styling",
"style": "text",
"value": [
{
"type": "ordgroup",
"mode": "math",
"value": [
{
"type": "mathord",
"loc": {
"end": 42,
"lexer": {
"input": "\\\\def\\\\arraystretch{1.5}\\\\begin{matrix}a&b\\\\\\\\c&d\\\\end{matrix}",
"pos": 56
},
"start": 41
},
"mode": "math",
"value": "c"
}
]
}
]
}
},
{
"type": "styling",
"mode": "math",
"value": {
"type": "styling",
"style": "text",
"value": [
{
"type": "ordgroup",
"mode": "math",
"value": [
{
"type": "mathord",
"loc": {
"end": 44,
"lexer": {
"input": "\\\\def\\\\arraystretch{1.5}\\\\begin{matrix}a&b\\\\\\\\c&d\\\\end{matrix}",
"pos": 56
},
"start": 43
},
"mode": "math",
"value": "d"
}
]
}
]
}
}
]
],
"hskipBeforeAndAfter": false,
"numHLinesBeforeRow": [
0,
0
],
"rowGaps": [
null
]
}
}
]
`;
exports[`A parser that does not throw on unsupported commands should build katex-error span for other type of KaTeX error 1`] = `
{
"attributes": {

View File

@@ -1173,6 +1173,12 @@ describe("A begin/end parser", function() {
const m3 = getParsed("\\begin{matrix}a&b\\\\ c&d \\\\ \\end{matrix}")[0];
expect(m3.value.body.length).toBe(2);
});
it("should grab \\arraystretch", function() {
const parse = getParsed("\\def\\arraystretch{1.5}" +
"\\begin{matrix}a&b\\\\c&d\\end{matrix}");
expect(parse).toMatchSnapshot();
});
});
describe("A sqrt parser", function() {