diff --git a/.gitignore b/.gitignore index fe58710f..86f594f5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ debugging .vscode vscode* table-generation/generated +.wireit diff --git a/CHANGELOG.md b/CHANGELOG.md index bf411630..d1992c62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### v1.6.1 - Pass `VisitInfo` as an additional argument ot `macroReplacers` and `environmentReplacers` in `unifiedLatexToHast`. - Allow skipping of HTML validation in `unifiedLatexToHast`. +- The `minted` environment parses its contents as a verbatim. ### v1.6.0 - Embellishment tokens are now supported in macro `signature`s. E.g., a `xxx: {signature: "e{^_}"}` will allow `\xxx_{foo}^{bar}` and `\xxx^{foo}_{bar}` to parse correctly. diff --git a/package-lock.json b/package-lock.json index c68ea76c..0373a3c5 100755 --- a/package-lock.json +++ b/package-lock.json @@ -6000,10 +6000,23 @@ "node": ">= 0.4" } }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true }, "node_modules/irregular-plurals": { @@ -6601,6 +6614,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -11475,9 +11494,9 @@ } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -12787,16 +12806,16 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", + "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", "dev": true, "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, @@ -14339,13 +14358,13 @@ } }, "node_modules/vite": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz", - "integrity": "sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.3.tgz", + "integrity": "sha512-UfmUD36DKkqhi/F75RrxvPpry+9+tTkrXfMNZD+SboZqBCMsxKtO52XeGzzuh7ioz+Eo/SYDBbdb0Z7vgcDJew==", "dev": true, "dependencies": { "esbuild": "^0.19.3", - "postcss": "^8.4.32", + "postcss": "^8.4.35", "rollup": "^4.2.0" }, "bin": { diff --git a/packages/unified-latex-ctan/package/minted/provides.ts b/packages/unified-latex-ctan/package/minted/provides.ts index 38f40e26..c3bc880e 100644 --- a/packages/unified-latex-ctan/package/minted/provides.ts +++ b/packages/unified-latex-ctan/package/minted/provides.ts @@ -17,4 +17,6 @@ export const macros: MacroInfoRecord = { newmintedfile: { signature: "o m m" }, }; -export const environments: EnvInfoRecord = {}; +export const environments: EnvInfoRecord = { + minted: { signature: "o m" }, +}; diff --git a/packages/unified-latex-util-parse/tests/parse-basic.test.ts b/packages/unified-latex-util-parse/tests/parse-basic.test.ts index fd1a2c72..f5977a90 100644 --- a/packages/unified-latex-util-parse/tests/parse-basic.test.ts +++ b/packages/unified-latex-util-parse/tests/parse-basic.test.ts @@ -140,104 +140,4 @@ describe("unified-latex-util-parse", () => { parsed = parseMath("\\x #1 y"); expect(printRaw(parsed)).toEqual("\\x #1 y"); }); - - it("Parses verbatim arguments directly as strings", () => { - expect(trimRenderInfo(parse("\\lstinline{some_code$}"))).toEqual({ - type: "root", - content: [ - m("lstinline", args([null, "some_code$"], { braces: "[]{}" })), - ], - }); - expect( - trimRenderInfo(parse("\\lstinline[language]{some_code$}")) - ).toEqual({ - type: "root", - content: [ - m( - "lstinline", - args(["language", "some_code$"], { braces: "[]{}" }) - ), - ], - }); - expect(trimRenderInfo(parse("\\lstinline#some_code$#"))).toEqual({ - type: "root", - content: [ - m("lstinline", [ - arg(null), - arg("some_code$", { openMark: "#", closeMark: "#" }), - ]), - ], - }); - expect( - trimRenderInfo(parse("\\lstinline[language]!some_code$!")) - ).toEqual({ - type: "root", - content: [ - m("lstinline", [ - arg("language", { braces: "[]" }), - arg("some_code$", { openMark: "!", closeMark: "!" }), - ]), - ], - }); - expect( - trimRenderInfo(parse("\\lstinline[foo %bar\n\n]{my code}")) - ).toEqual({ - type: "root", - content: [ - m("lstinline", [ - arg( - [ - s("foo"), - { - type: "comment", - content: "bar", - leadingWhitespace: true, - sameline: true, - suffixParbreak: true, - }, - { type: "parbreak" }, - ], - { braces: "[]" } - ), - arg("my code"), - ]), - ], - }); - expect( - trimRenderInfo( - parse("\\lstinline{code % also code\n\\still code\\\\}") - ) - ).toEqual({ - type: "root", - content: [ - m("lstinline", [ - arg(null), - arg("code % also code\n\\still code\\\\"), - ]), - ], - }); - - expect( - trimRenderInfo(parse("\\mint[options]{language}#some_code$#")) - ).toEqual({ - type: "root", - content: [ - m("mint", [ - ...args(["options", "language"], { braces: "[]{}" }), - arg("some_code$", { openMark: "#", closeMark: "#" }), - ]), - ], - }); - expect( - trimRenderInfo(parse("\\mintinline[options]{language}#some_code$#")) - ).toEqual({ - type: "root", - content: [ - m("mintinline", [ - ...args(["options", "language"], { braces: "[]{}" }), - arg("some_code$", { openMark: "#", closeMark: "#" }), - ]), - ], - }); - }); }); diff --git a/packages/unified-latex-util-parse/tests/parse-varbatim.test.ts b/packages/unified-latex-util-parse/tests/parse-varbatim.test.ts new file mode 100644 index 00000000..a7864b5b --- /dev/null +++ b/packages/unified-latex-util-parse/tests/parse-varbatim.test.ts @@ -0,0 +1,154 @@ +import util from "util"; +import { parse } from "../libs/parse"; +import { printRaw } from "@unified-latex/unified-latex-util-print-raw"; +import { parseMath } from "../libs/parse-math"; +import { trimRenderInfo } from "@unified-latex/unified-latex-util-render-info"; +import { SP, arg, args, m, s, env } from "@unified-latex/unified-latex-builder"; + +/* eslint-env jest */ + +// Make console.log pretty-print by default +const origLog = console.log; +console.log = (...args) => { + origLog(...args.map((x) => util.inspect(x, false, 10, true))); +}; + +describe("unified-latex-util-parse", () => { + it("Parses verbatim arguments from `minted` directly as strings", () => { + expect(trimRenderInfo(parse("\\lstinline{some_code$}"))).toEqual({ + type: "root", + content: [ + m("lstinline", args([null, "some_code$"], { braces: "[]{}" })), + ], + }); + expect( + trimRenderInfo(parse("\\lstinline[language]{some_code$}")) + ).toEqual({ + type: "root", + content: [ + m( + "lstinline", + args(["language", "some_code$"], { braces: "[]{}" }) + ), + ], + }); + expect(trimRenderInfo(parse("\\lstinline#some_code$#"))).toEqual({ + type: "root", + content: [ + m("lstinline", [ + arg(null), + arg("some_code$", { openMark: "#", closeMark: "#" }), + ]), + ], + }); + expect( + trimRenderInfo(parse("\\lstinline[language]!some_code$!")) + ).toEqual({ + type: "root", + content: [ + m("lstinline", [ + arg("language", { braces: "[]" }), + arg("some_code$", { openMark: "!", closeMark: "!" }), + ]), + ], + }); + expect( + trimRenderInfo(parse("\\lstinline[foo %bar\n\n]{my code}")) + ).toEqual({ + type: "root", + content: [ + m("lstinline", [ + arg( + [ + s("foo"), + { + type: "comment", + content: "bar", + leadingWhitespace: true, + sameline: true, + suffixParbreak: true, + }, + { type: "parbreak" }, + ], + { braces: "[]" } + ), + arg("my code"), + ]), + ], + }); + expect( + trimRenderInfo( + parse("\\lstinline{code % also code\n\\still code\\\\}") + ) + ).toEqual({ + type: "root", + content: [ + m("lstinline", [ + arg(null), + arg("code % also code\n\\still code\\\\"), + ]), + ], + }); + + expect( + trimRenderInfo(parse("\\mint[options]{language}#some_code$#")) + ).toEqual({ + type: "root", + content: [ + m("mint", [ + ...args(["options", "language"], { braces: "[]{}" }), + arg("some_code$", { openMark: "#", closeMark: "#" }), + ]), + ], + }); + expect( + trimRenderInfo(parse("\\mintinline[options]{language}#some_code$#")) + ).toEqual({ + type: "root", + content: [ + m("mintinline", [ + ...args(["options", "language"], { braces: "[]{}" }), + arg("some_code$", { openMark: "#", closeMark: "#" }), + ]), + ], + }); + }); + + it("parses verbatim environments from `minted`", () => { + expect( + trimRenderInfo( + parse("\\begin{minted}{python}some_code$\\end{minted}") + ) + ).toEqual({ + type: "root", + content: [ + env( + "minted", + [s("some_code$")], + [ + arg([], { openMark: "", closeMark: "" }), + arg("python", { braces: "{}" }), + ] + ), + ], + }); + + expect( + trimRenderInfo( + parse("\\begin{minted}[foo]{python}some_code$\\end{minted}") + ) + ).toEqual({ + type: "root", + content: [ + env( + "minted", + [s("some_code$")], + [ + arg("foo", { braces: "[]" }), + arg("python", { braces: "{}" }), + ] + ), + ], + }); + }); +}); diff --git a/packages/unified-latex-util-pegjs/grammars/latex.pegjs b/packages/unified-latex-util-pegjs/grammars/latex.pegjs index 1ee9a773..ea703117 100644 --- a/packages/unified-latex-util-pegjs/grammars/latex.pegjs +++ b/packages/unified-latex-util-pegjs/grammars/latex.pegjs @@ -126,6 +126,7 @@ special_macro "special macro" // for the special macros like \[ \] and \begin{} // verbatim macro in minted package / verbatim_minted // verbatim environment + / verbatim_minted_environment / verbatim_environment // display math with \[...\] / begin_display_math @@ -221,22 +222,28 @@ verbatim_minted_environment "verbatim minted environment" begin_group env:"minted" end_group - lang:group - body:( + option:square_bracket_argument? + language:group + body:$( !( end_env end_env:group & { return compare_env({ content: [env] }, end_env); } ) - x:. { return x; } + . )* end_env begin_group - verbatim_env_name + "minted" end_group { - return createNode("verbatim", { - env: `${env}{${lang.content.content}}`, - content: body.join(""), + const content = [ + ...(option || []), + language, + { type: "string", content: body }, + ]; + return createNode("environment", { + env, + content, }); } @@ -245,7 +252,7 @@ verbatim_environment "verbatim environment" begin_group env:verbatim_env_name end_group - body:( + body:$( !( end_env end_env:group @@ -258,8 +265,8 @@ verbatim_environment "verbatim environment" verbatim_env_name end_group { return createNode("verbatim", { - env: env, - content: body.join(""), + env, + content: body, }); }