Create globalGroup option to place definitions in global scope (#2091)

If true, this allows \newcommand definitions to persist across calls to
renderToString.

Fix https://github.com/KaTeX/KaTeX/issues/2070
This commit is contained in:
Ciro Santilli,Opinions and content are my own, not my employer's,2018新疆改造中心,1989六四事件,1999法轮功 ,2019 996.ICU, 2018包子露宪,2015 710律师劫,2015巴拿马文件 邓家贵,2017低端人口,2008西藏骚乱scriptalert(1)/script
2019-09-22 20:03:44 +01:00
committed by Kevin Barabash
parent f03671ce61
commit 36595343b7
4 changed files with 31 additions and 4 deletions

View File

@@ -51,6 +51,7 @@ You can provide an object of options as the last argument to [`katex.render` and
- Allow all commands with specific protocols: `trust: (context) => ['http', 'https', '_relative'].includes(context.protocol)`
- Allow all commands but forbid specific protocol: `trust: (context) => context.protocol !== 'file'`
- Allow certain commands with specific protocols: `trust: (context) => ['\\url', '\\href'].includes(context.command) && ['http', 'https', '_relative'].includes(context.protocol)`
- `globalGroup`: `boolean` (default: `false`). Place KaTeX code in the global group. As a consequence, `\def` and `\newcommand` persist in `macros` across render calls. In LaTeX, constructs such as `\begin{equation}` and `$$` create a local group and prevent definitions from becoming visible outside of those blocks.
For example:

View File

@@ -115,9 +115,11 @@ export default class Parser {
* Main parsing function, which parses an entire input.
*/
parse(): AnyParseNode[] {
// Create a group namespace for the math expression.
// (LaTeX creates a new group for every $...$, $$...$$, \[...\].)
this.gullet.beginGroup();
if (!this.settings.globalGroup) {
// Create a group namespace for the math expression.
// (LaTeX creates a new group for every $...$, $$...$$, \[...\].)
this.gullet.beginGroup();
}
// Use old \color behavior (same as LaTeX's \textcolor) if requested.
// We do this within the group for the math expression, so it doesn't
@@ -133,7 +135,9 @@ export default class Parser {
this.expect("EOF");
// End the group namespace for the expression
this.gullet.endGroup();
if (!this.settings.globalGroup) {
this.gullet.endGroup();
}
return parse;
}

View File

@@ -50,6 +50,7 @@ export type SettingsOptions = {
trust?: boolean | TrustFunction;
maxSize?: number;
maxExpand?: number;
globalGroup?: boolean;
};
/**
@@ -76,6 +77,7 @@ export default class Settings {
trust: boolean | TrustFunction;
maxSize: number;
maxExpand: number;
globalGroup: boolean;
constructor(options: SettingsOptions) {
// allow null options
@@ -96,6 +98,7 @@ export default class Settings {
this.trust = utils.deflt(options.trust, false);
this.maxSize = Math.max(0, utils.deflt(options.maxSize, Infinity));
this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000));
this.globalGroup = utils.deflt(options.globalGroup, false);
}
/**

View File

@@ -3162,6 +3162,25 @@ describe("A macro expander", function() {
expect(macros["\\foo"]).toBeFalsy();
});
it("\\def changes settings.macros with globalGroup", () => {
const macros = {};
expect`\gdef\foo{1}`.toParse(new Settings({macros, globalGroup: true}));
expect(macros["\\foo"]).toBeTruthy();
});
it("\\newcommand doesn't change settings.macros", () => {
const macros = {};
expect`\newcommand\foo{x^2}\foo+\foo`.toParse(new Settings({macros}));
expect(macros["\\foo"]).toBeFalsy();
});
it("\\newcommand changes settings.macros with globalGroup", () => {
const macros = {};
expect`\newcommand\foo{x^2}\foo+\foo`.toParse(
new Settings({macros, globalGroup: true}));
expect(macros["\\foo"]).toBeTruthy();
});
it("\\newcommand defines new macros", () => {
expect`\newcommand\foo{x^2}\foo+\foo`.toParseLike`x^2+x^2`;
expect`\newcommand{\foo}{x^2}\foo+\foo`.toParseLike`x^2+x^2`;