diff --git a/README.md b/README.md index ced83c1..13f8f64 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ import markdownItMath from "markdown-it-math"; // Optional (with defaults) const options = { - inlineDelimiters: ["$", ["$`", "`$"]] + inlineDelimiters: ["$", ["$`", "`$"]], + inlineAllowWhiteSpacePadding: false, blockDelimiters: "$$", defaultRendererOptions, inlineCustomElement, // see below @@ -146,6 +147,11 @@ default renderer. strings or pairs containing empty strings are ignored. If no valid strings or pairs are provided, it will turn off the rule. Default ``["$", ["$`", "`$"]]``. +- `inlineAllowWhiteSpacePadding`: Whether to allow whitespace + immediately after the opening delimiter and immediately before the + closing delimiter. You may want this if you use e.g. ``$`...`$`` or + `\(...\)` as delimiters where the risk of non-intended math + expression is low. - `blockDelimiters`: Same as above, but for block expressions. Default `"$$"`. - `defaultRendererOptions`: The options passed into the default renderer. Ignored if you use a custom renderer. Default `{}` @@ -299,7 +305,7 @@ const md = markdownIt().use(markdownItMath, { return temml.renderToString(src); }, - blockDelimiters: [$$, ["\\[", "\\]"]], + blockDelimiters: ["$$", ["\\[", "\\]"]], blockRenderer(src, token) { if (token.markup === "$$") { return mathup(src, { display: "block" }).toString(); diff --git a/index.js b/index.js index f64dabb..afec3f5 100644 --- a/index.js +++ b/index.js @@ -57,10 +57,12 @@ function fromDelimiterOption(delimiters) { } /** - * @param {Array<[string, string]>} delimiters + * @param {object} options + * @param {Array<[string, string]>} options.delimiters + * @param {boolean} options.allowWhiteSpacePadding * @returns {RuleInline} */ -function createInlineMathRule(delimiters) { +function createInlineMathRule({ delimiters, allowWhiteSpacePadding }) { return (state, silent) => { const start = state.pos; @@ -76,8 +78,11 @@ function createInlineMathRule(delimiters) { for (const [open, close] of markers) { const pos = start + open.length; - if (state.md.utils.isWhiteSpace(state.src.charCodeAt(pos))) { - // Don’t allow whitespace immediately after open delimiter ... for now. + if ( + state.md.utils.isWhiteSpace(state.src.charCodeAt(pos)) && + !allowWhiteSpacePadding + ) { + // Don’t allow whitespace immediately after open delimiter continue; } @@ -88,12 +93,19 @@ function createInlineMathRule(delimiters) { continue; } - // Don’t allow whitespace immediately before close delimiter ... for now. - if (state.md.utils.isWhiteSpace(state.src.charCodeAt(matchStart - 1))) { + if ( + state.md.utils.isWhiteSpace(state.src.charCodeAt(matchStart - 1)) && + !allowWhiteSpacePadding + ) { + // Don’t allow whitespace immediately before close delimiter continue; } - const content = state.src.slice(pos, matchStart).replaceAll("\n", " "); + let content = state.src.slice(pos, matchStart).replaceAll("\n", " "); + + if (allowWhiteSpacePadding) { + content = content.replace(/^ (.+) $/, "$1"); + } if (!silent) { const token = state.push("math_inline", "math", 0); @@ -281,6 +293,7 @@ function defaultBlockRenderer(options, md) { * @property {string} [inlineClose] - Deprecated: Use inlineDelimiters * @property {CustomElementOption} [inlineCustomElement] - If you want to render to a custom element. * @property {Renderer} [inlineRenderer] - Custom renderer for inline math. Default mathup. + * @property {boolean} [inlineAllowWhiteSpacePadding] - If you want allow inline math to start or end with whitespace. * @property {string | Delimiter[]} [blockDelimiters] - Block math delimters. * @property {string} [blockOpen] - Deprecated: Use blockDelimiters * @property {string} [blockClose] - Deprecated: Use blockDelimiters @@ -295,6 +308,7 @@ export default function markdownItMath( { defaultRendererOptions = {}, + inlineAllowWhiteSpacePadding = false, inlineOpen, inlineClose, inlineDelimiters = inlineOpen && inlineClose @@ -320,7 +334,10 @@ export default function markdownItMath( ) { const inlineDelimitersArray = fromDelimiterOption(inlineDelimiters); if (inlineDelimitersArray) { - const inlineMathRule = createInlineMathRule(inlineDelimitersArray); + const inlineMathRule = createInlineMathRule({ + delimiters: inlineDelimitersArray, + allowWhiteSpacePadding: inlineAllowWhiteSpacePadding, + }); md.inline.ruler.before("escape", "math_inline", inlineMathRule); diff --git a/test/test.js b/test/test.js index db32b25..46ca8e3 100644 --- a/test/test.js +++ b/test/test.js @@ -371,8 +371,10 @@ $$ }); suite("Options", () => { - test("Thick dollar delims", (t) => { + test("Thick dollar delims", () => { const md = markdownIt().use(markdownItMath, { + inlineCustomElement, + blockCustomElement, inlineDelimiters: "$$", blockDelimiters: "$$$", }); @@ -384,10 +386,13 @@ $$$ $$$ `; - t.assert.snapshot(md.render(src)); + assert.equal( + md.render(src), + [p(`Foo ${mathspan("1+1 = 2")} bar`), mathblock("1+1 = 2")].join(""), + ); }); - test("No delimiters turns off rules", (t) => { + test("No delimiters turns off rules", () => { const md = markdownIt().use(markdownItMath, { inlineDelimiters: "", blockDelimiters: [], @@ -400,13 +405,15 @@ $$ $$ `; - t.assert.snapshot(md.render(src)); + assert.equal(md.render(src), p("Foo $1+1 = 2$ bar", "$$\n1+1 = 2\n$$")); }); - test("Empty open or close dilimeters are filtered out", (t) => { + test("Empty open or close dilimeters are filtered out", () => { const md = markdownIt().use(markdownItMath, { - inlineDelimiters: ["", ["", "$"]], + blockCustomElement, blockDelimiters: [["$$", ""]], + inlineCustomElement, + inlineDelimiters: ["", ["", "$"]], }); const src = `Foo $1+1 = 2$ bar @@ -416,21 +423,37 @@ $$ $$ `; - t.assert.snapshot(md.render(src)); + assert.equal(md.render(src), p("Foo $1+1 = 2$ bar", "$$\n1+1 = 2\n$$")); }); - test("Space dollar delims", (t) => { + test("Space dollar delims", () => { const md = markdownIt().use(markdownItMath, { + blockCustomElement, + inlineCustomElement, inlineDelimiters: [["$ ", " $"]], }); - const src = `Foo $ 1+1 = 2 $ bar`; + const src = `foo $ 1+1 = 2 $ bar`; - t.assert.snapshot(md.render(src)); + assert.equal(md.render(src), p(`foo ${mathspan("1+1 = 2")} bar`)); + }); + + test("Allow inline space padding", () => { + const md = markdownIt().use(markdownItMath, { + blockCustomElement, + inlineCustomElement, + inlineAllowWhiteSpacePadding: true, + }); + + const src = "foo $` 1+1 = 2 `$ bar"; + + assert.equal(md.render(src), p(`foo ${mathspan("1+1 = 2")} bar`)); }); - test("LaTeX style delims", (t) => { + test("LaTeX style delims", () => { const md = markdownIt().use(markdownItMath, { + inlineCustomElement, + blockCustomElement, inlineDelimiters: [["\\(", "\\)"]], blockDelimiters: [["\\[", "\\]"]], }); @@ -442,7 +465,10 @@ $$ \] `; - t.assert.snapshot(md.render(src)); + assert.equal( + md.render(src), + [p(`Foo ${mathspan("1+1 = 2")} bar`), mathblock("1+1 = 2")].join(""), + ); }); test("Different options for the default renderer", (t) => { @@ -538,17 +564,22 @@ $$`; }); suite("Depricated", () => { - test("inlineOpen inlineClose blockOpen blockClose", (t) => { - const md = markdownIt().use(markdownItMath, { + test("inlineOpen inlineClose blockOpen blockClose", () => { + const mdDepricated = markdownIt().use(markdownItMath, { inlineOpen: "$((", inlineClose: "))$", blockOpen: "$[[", blockClose: "]]$", }); + const mdRecommended = markdownIt().use(markdownItMath, { + inlineDelimiters: [["$((", "))$"]], + blockDelimiters: [["$[[", "]]$"]], + }); + const src = '$(("inline"))$\n$[["block"]]$'; - t.assert.snapshot(md.render(src)); + assert.equal(mdDepricated.render(src), mdRecommended.render(src)); }); }); }); diff --git a/test/test.js.snapshot b/test/test.js.snapshot index 4b15c4e..c1a06b9 100644 --- a/test/test.js.snapshot +++ b/test/test.js.snapshot @@ -1,31 +1,7 @@ -exports[`Options > Depricated > inlineOpen inlineClose blockOpen blockClose 1`] = ` -"
\\n\\n" -`; - exports[`Options > Different options for the default renderer 1`] = ` "\\n\\n" `; -exports[`Options > Empty open or close dilimeters are filtered out 1`] = ` -"Foo $1+1 = 2$ bar
\\n$$\\n1+1 = 2\\n$$
\\n" -`; - -exports[`Options > LaTeX style delims 1`] = ` -"Foo bar
\\n\\n" -`; - -exports[`Options > No delimiters turns off rules 1`] = ` -"Foo $1+1 = 2$ bar
\\n$$\\n1+1 = 2\\n$$
\\n" -`; - -exports[`Options > Space dollar delims 1`] = ` -"Foo bar
\\n" -`; - -exports[`Options > Thick dollar delims 1`] = ` -"Foo bar
\\n\\n" -`; - exports[`Options > Use Temml as renderer > block 1`] = ` "\\n" `;