From 40109f624805af41c42897cd428b83a93587f58d Mon Sep 17 00:00:00 2001 From: ylemkimon Date: Sun, 31 Oct 2021 12:46:43 +0900 Subject: [PATCH] feat: implement \relax as no-op function (#3384) * feat: implement \relax as no-op function BREAKING CHANGE: `\relax` is now implemented as a function. It'll stop expansions and parsing, so the behavior around `\relax` may change. For example, `\kern2\relax em` will no longer work. --- docs/migration.md | 6 ++++++ src/MacroExpander.js | 10 +++------- src/functions.js | 1 + src/functions/relax.js | 17 +++++++++++++++++ src/macros.js | 2 +- test/katex-spec.js | 6 ++++++ 6 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 src/functions/relax.js diff --git a/docs/migration.md b/docs/migration.md index 06b9908d..852c23ca 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -3,6 +3,12 @@ id: migration title: Migration Guide --- +## v0.15.0 + +`\relax` is now implemented as a function. It'll stop expansions and parsing, +so the behavior around `\relax` may change. For example, `\kern2\relax em` will +no longer work. + ## v0.14.0 With module loaders that support conditional exports and ECMAScript modules, diff --git a/src/MacroExpander.js b/src/MacroExpander.js index ce41e452..5de92d99 100644 --- a/src/MacroExpander.js +++ b/src/MacroExpander.js @@ -20,7 +20,6 @@ import type Settings from "./Settings"; // List of commands that act like macros but aren't defined as a macro, // function, or symbol. Used in `isDefined`. export const implicitCommands = { - "\\relax": true, // MacroExpander.js "^": true, // Parser.js "_": true, // Parser.js "\\limits": true, // Parser.js @@ -333,15 +332,12 @@ export default class MacroExpander implements MacroContextInterface { const expanded = this.expandOnce(); // expandOnce returns Token if and only if it's fully expanded. if (expanded instanceof Token) { - // \relax stops the expansion, but shouldn't get returned (a - // null return value couldn't get implemented as a function). // the token after \noexpand is interpreted as if its meaning // were ‘\relax’ - if (expanded.text === "\\relax" || expanded.treatAsRelax) { - this.stack.pop(); - } else { - return this.stack.pop(); // === expanded + if (expanded.treatAsRelax) { + expanded.text = "\\relax"; } + return this.stack.pop(); // === expanded } } diff --git a/src/functions.js b/src/functions.js index add2d0b6..edcb9bf1 100644 --- a/src/functions.js +++ b/src/functions.js @@ -37,6 +37,7 @@ import "./functions/ordgroup"; import "./functions/overline"; import "./functions/phantom"; import "./functions/raisebox"; +import "./functions/relax"; import "./functions/rule"; import "./functions/sizing"; import "./functions/smash"; diff --git a/src/functions/relax.js b/src/functions/relax.js new file mode 100644 index 00000000..c55c4c75 --- /dev/null +++ b/src/functions/relax.js @@ -0,0 +1,17 @@ +//@flow +import defineFunction from "../defineFunction"; + +defineFunction({ + type: "internal", + names: ["\\relax"], + props: { + numArgs: 0, + allowedInText: true, + }, + handler({parser}) { + return { + type: "internal", + mode: parser.mode, + }; + }, +}); diff --git a/src/macros.js b/src/macros.js index e96a2194..7fd7e2ca 100644 --- a/src/macros.js +++ b/src/macros.js @@ -368,7 +368,7 @@ defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"); // \renewcommand{\colon}{\nobreak\mskip2mu\mathpunct{}\nonscript // \mkern-\thinmuskip{:}\mskip6muplus1mu\relax} defineMacro("\\colon", "\\nobreak\\mskip2mu\\mathpunct{}" + - "\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu"); + "\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"); // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} defineMacro("\\boxed", "\\fbox{$\\displaystyle{#1}$}"); diff --git a/test/katex-spec.js b/test/katex-spec.js index 42bba6ba..1b62c4ac 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -4046,3 +4046,9 @@ describe("debugging macros", () => { }); }); }); + +describe("\\relax", () => { + it("should stop the expansion", () => { + expect`\kern2\relax em`.not.toParse(); + }); +});