diff --git a/src/environments.js b/src/environments.js index ff294ca7a..c323d433e 100644 --- a/src/environments.js +++ b/src/environments.js @@ -38,141 +38,141 @@ function parseArray(parser, pos, mode, result) { } /* - * An environment definition is very similar to a function definition. - * Each element of the following array may contain - * - names: The names associated with a function. This can be used to - * share one implementation between several similar environments. + * An environment definition is very similar to a function definition: + * it is declared with a name or a list of names, a set of properties + * and a handler containing the actual implementation. + * + * The properties include: * - numArgs: The number of arguments after the \begin{name} function. * - argTypes: (optional) Just like for a function * - allowedInText: (optional) Whether or not the environment is allowed inside * text mode (default false) (not enforced yet) * - numOptionalArgs: (optional) Just like for a function - * - handler: The function that is called to handle this environment. - * It will receive the following arguments: - * - pos: the current position of the parser. - * - mode: the current parsing mode. - * - envName: the name of the environment, one of the listed names. - * - [args]: the arguments passed to \begin. - * - positions: the positions associated with these arguments. + * A bare number instead of that object indicates the numArgs value. + * + * The handler function will receive the following arguments: + * - pos: the current position of the parser. + * - mode: the current parsing mode. + * - envName: the name of the environment, one of the listed names. + * - [args]: the arguments passed to \begin. + * - positions: the positions associated with these arguments. + * The handler is called with `this` referring to the parser. + * It must return a ParseResult. */ -var environmentDefinitions = [ - - // Arrays are part of LaTeX, defined in lttab.dtx so its documentation - // is part of the source2e.pdf file of LaTeX2e source documentation. - { - names: ["array"], - numArgs: 1, - handler: function(pos, mode, envName, colalign, positions) { - var parser = this; - colalign = colalign.value.map ? colalign.value : [colalign]; - var cols = colalign.map(function(node) { - var ca = node.value; - if ("lcr".indexOf(ca) !== -1) { - return { - type: "align", - align: ca - }; - } else if (ca === "|") { - return { - type: "separator", - separator: "|" - }; - } - throw new ParseError( - "Unknown column alignment: " + node.value, - parser.lexer, positions[1]); - }); - var res = { - type: "array", - cols: cols, - hskipBeforeAndAfter: true // \@preamble in lttab.dtx - }; - res = parseArray(parser, pos, mode, res); - return res; - } - }, - - // The matrix environments of amsmath builds on the array environment - // of LaTeX, which is discussed above. - { - names: [ - "matrix", - "pmatrix", - "bmatrix", - "Bmatrix", - "vmatrix", - "Vmatrix" - ], - handler: function(pos, mode, envName) { - var delimiters = { - "matrix": null, - "pmatrix": ["(", ")"], - "bmatrix": ["[", "]"], - "Bmatrix": ["\\{", "\\}"], - "vmatrix": ["|", "|"], - "Vmatrix": ["\\Vert", "\\Vert"] - }[envName]; - var res = { - type: "array", - hskipBeforeAndAfter: false // \hskip -\arraycolsep in amsmath - }; - res = parseArray(this, pos, mode, res); - if (delimiters) { - res.result = new ParseNode("leftright", { - body: [res.result], - left: delimiters[0], - right: delimiters[1] - }, mode); - } - return res; - } - }, - - // A cases environment (in amsmath.sty) is almost equivalent to - // \def\arraystretch{1.2}% - // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. - { - names: ["cases"], - handler: function(pos, mode, envName) { - var res = { - type: "array", - arraystretch: 1.2, - cols: [{ - type: "align", - align: "l", - pregap: 0, - postgap: fontMetrics.metrics.quad - }, { - type: "align", - align: "l", - pregap: 0, - postgap: 0 - }] - }; - res = parseArray(this, pos, mode, res); - res.result = new ParseNode("leftright", { - body: [res.result], - left: "\\{", - right: "." - }, mode); - return res; - } +function defineEnvironment(names, props, handler) { + if (typeof names === "string") { + names = [names]; } -]; - -module.exports = (function() { - // nested function so we don't leak i and j into the module scope - var exports = {}; - for (var i = 0; i < environmentDefinitions.length; ++i) { - var def = environmentDefinitions[i]; - def.greediness = 1; - def.allowedInText = !!def.allowedInText; - def.numArgs = def.numArgs || 0; - def.numOptionalArgs = def.numOptionalArgs || 0; - for (var j = 0; j < def.names.length; ++j) { - exports[def.names[j]] = def; - } + if (typeof props === "number") { + props = { numArgs: props }; } - return exports; -})(); + // Set default values of environments + var data = { + numArgs: props.numArgs || 0, + argTypes: props.argTypes, + greediness: 1, + allowedInText: !!props.allowedInText, + numOptionalArgs: props.numOptionalArgs || 0, + handler: handler + }; + for (var i = 0; i < names.length; ++i) { + module.exports[names[i]] = data; + } +} + +// Arrays are part of LaTeX, defined in lttab.dtx so its documentation +// is part of the source2e.pdf file of LaTeX2e source documentation. +defineEnvironment("array", { + numArgs: 1 +}, function(pos, mode, envName, colalign, positions) { + var parser = this; + colalign = colalign.value.map ? colalign.value : [colalign]; + var cols = colalign.map(function(node) { + var ca = node.value; + if ("lcr".indexOf(ca) !== -1) { + return { + type: "align", + align: ca + }; + } else if (ca === "|") { + return { + type: "separator", + separator: "|" + }; + } + throw new ParseError( + "Unknown column alignment: " + node.value, + parser.lexer, positions[1]); + }); + var res = { + type: "array", + cols: cols, + hskipBeforeAndAfter: true // \@preamble in lttab.dtx + }; + res = parseArray(parser, pos, mode, res); + return res; +}); + +// The matrix environments of amsmath builds on the array environment +// of LaTeX, which is discussed above. +defineEnvironment([ + "matrix", + "pmatrix", + "bmatrix", + "Bmatrix", + "vmatrix", + "Vmatrix" +], { +}, function(pos, mode, envName) { + var delimiters = { + "matrix": null, + "pmatrix": ["(", ")"], + "bmatrix": ["[", "]"], + "Bmatrix": ["\\{", "\\}"], + "vmatrix": ["|", "|"], + "Vmatrix": ["\\Vert", "\\Vert"] + }[envName]; + var res = { + type: "array", + hskipBeforeAndAfter: false // \hskip -\arraycolsep in amsmath + }; + res = parseArray(this, pos, mode, res); + if (delimiters) { + res.result = new ParseNode("leftright", { + body: [res.result], + left: delimiters[0], + right: delimiters[1] + }, mode); + } + return res; +}); + +// A cases environment (in amsmath.sty) is almost equivalent to +// \def\arraystretch{1.2}% +// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right. +defineEnvironment("cases", { +}, function(pos, mode, envName) { + var res = { + type: "array", + arraystretch: 1.2, + cols: [{ + type: "align", + align: "l", + pregap: 0, + postgap: fontMetrics.metrics.quad + }, { + type: "align", + align: "l", + pregap: 0, + postgap: 0 + }] + }; + res = parseArray(this, pos, mode, res); + res.result = new ParseNode("leftright", { + body: [res.result], + left: "\\{", + right: "." + }, mode); + return res; +});