diff --git a/changelog_unreleased/markdown/16596.md b/changelog_unreleased/markdown/16596.md new file mode 100644 index 000000000..35a7cc4ed --- /dev/null +++ b/changelog_unreleased/markdown/16596.md @@ -0,0 +1,35 @@ +#### Fix adjacent markdown syntax in blockquote (#16596 by @fiji-flo) + + +```md + +> `x` +> `y` + +> _x_ +> _y_ + +> [foo](http://foo) +> [bar](http://bar) + +> `this` behaves +> `correctly` + + +> `x` > `y` + +> _x_ > _y_ + +> [foo](http://foo) > [bar](http://bar) + +> `this` behaves `correctly` + + +> `x` `y` + +> _x_ _y_ + +> [foo](http://foo) [bar](http://bar) + +> `this` behaves `correctly` +``` diff --git a/src/language-markdown/print-preprocess.js b/src/language-markdown/print-preprocess.js index 14a5543f9..ec283a8a2 100644 --- a/src/language-markdown/print-preprocess.js +++ b/src/language-markdown/print-preprocess.js @@ -2,7 +2,8 @@ import htmlWhitespaceUtils from "../utils/html-whitespace-utils.js"; import { getOrderedListItemInfo, mapAst, splitText } from "./utils.js"; // 0x0 ~ 0x10ffff -const isSingleCharRegex = /^.$/su; +const isSingleCharRegex = /^\\?.$/su; +const isNewLineBlockquoteRegex = /^\n *>[ >]*$/u; function preprocess(ast, options) { ast = restoreUnescapedCharacter(ast, options); @@ -14,21 +15,33 @@ function preprocess(ast, options) { } function restoreUnescapedCharacter(ast, options) { - return mapAst(ast, (node) => - node.type !== "text" || - node.value === "*" || - node.value === "_" || // handle these cases in printer - !isSingleCharRegex.test(node.value) || - node.position.end.offset - node.position.start.offset === node.value.length - ? node - : { - ...node, - value: options.originalText.slice( - node.position.start.offset, - node.position.end.offset, - ), - }, - ); + return mapAst(ast, (node) => { + if (node.type !== "text") { + return node; + } + + const { value } = node; + + if ( + value === "*" || + value === "_" || // handle these cases in printer + !isSingleCharRegex.test(value) || + node.position.end.offset - node.position.start.offset === value.length + ) { + return node; + } + + const text = options.originalText.slice( + node.position.start.offset, + node.position.end.offset, + ); + + if (isNewLineBlockquoteRegex.test(text)) { + return node; + } + + return { ...node, value: text }; + }); } function mergeChildren(ast, shouldMerge, mergeNode) { diff --git a/tests/format/markdown/blockquote/__snapshots__/format.test.js.snap b/tests/format/markdown/blockquote/__snapshots__/format.test.js.snap index 4ea266234..7827f76f6 100644 --- a/tests/format/markdown/blockquote/__snapshots__/format.test.js.snap +++ b/tests/format/markdown/blockquote/__snapshots__/format.test.js.snap @@ -43,6 +43,90 @@ proseWrap: "always" ================================================================================ `; +exports[`code.md - {"proseWrap":"never"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "never" + | printWidth +=====================================input====================================== +> NOTE: To use \`unobtrusive\`, \`unobtrusive/import\`, \`unobtrusive/react\`, and \`unobtrusive/flowtype\` together, your eslint config would look like this: +>\`\`\`json +>{ +> "extends": [ +> "unobtrusive", +> "unobtrusive/import", +> "unobtrusive/react", +> "unobtrusive/flowtype" +> ], +> "env": { +> "browser": true +> } +>} +>\`\`\` + +=====================================output===================================== +> NOTE: To use \`unobtrusive\`, \`unobtrusive/import\`, \`unobtrusive/react\`, and \`unobtrusive/flowtype\` together, your eslint config would look like this: +> +> \`\`\`json +> { +> "extends": [ +> "unobtrusive", +> "unobtrusive/import", +> "unobtrusive/react", +> "unobtrusive/flowtype" +> ], +> "env": { +> "browser": true +> } +> } +> \`\`\` + +================================================================================ +`; + +exports[`code.md - {"proseWrap":"preserve"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "preserve" + | printWidth +=====================================input====================================== +> NOTE: To use \`unobtrusive\`, \`unobtrusive/import\`, \`unobtrusive/react\`, and \`unobtrusive/flowtype\` together, your eslint config would look like this: +>\`\`\`json +>{ +> "extends": [ +> "unobtrusive", +> "unobtrusive/import", +> "unobtrusive/react", +> "unobtrusive/flowtype" +> ], +> "env": { +> "browser": true +> } +>} +>\`\`\` + +=====================================output===================================== +> NOTE: To use \`unobtrusive\`, \`unobtrusive/import\`, \`unobtrusive/react\`, and \`unobtrusive/flowtype\` together, your eslint config would look like this: +> +> \`\`\`json +> { +> "extends": [ +> "unobtrusive", +> "unobtrusive/import", +> "unobtrusive/react", +> "unobtrusive/flowtype" +> ], +> "env": { +> "browser": true +> } +> } +> \`\`\` + +================================================================================ +`; + exports[`ignore-code.md - {"proseWrap":"always"} format 1`] = ` ====================================options===================================== parsers: ["markdown"] @@ -158,6 +242,236 @@ proseWrap: "always" ================================================================================ `; +exports[`ignore-code.md - {"proseWrap":"never"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "never" + | printWidth +=====================================input====================================== +> \`\`\`\`md +> +> \`\`\`js +> ugly ( code ) ; +> \`\`\` +> \`\`\`\` + +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +> - test +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +\`\`\`\`md +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` +\`\`\`\` + +> \`\`\`\`md +> > \`\`\`md +> > +> > - This is a long long +> > long long long long +> > long long paragraph. +> > \`\`\` +> \`\`\`\` + +> +> +> - This is a long long +> long long long long +> long long paragraph. +> + +> \`\`\`\`js +> // prettier-ignore +> const x = 1, +> b = 2 +> \`\`\`\` + +=====================================output===================================== +> \`\`\`\`md +> +> \`\`\`js +> ugly ( code ) ; +> \`\`\` +> \`\`\`\` + +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +> - test +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +\`\`\`\`md +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` +\`\`\`\` + +> \`\`\`\`md +> > \`\`\`md +> > +> > - This is a long long +> > long long long long +> > long long paragraph. +> > \`\`\` +> \`\`\`\` + +> +> - This is a long long +> long long long long +> long long paragraph. + +> \`\`\`js +> // prettier-ignore +> const x = 1, +> b = 2 +> \`\`\` + +================================================================================ +`; + +exports[`ignore-code.md - {"proseWrap":"preserve"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "preserve" + | printWidth +=====================================input====================================== +> \`\`\`\`md +> +> \`\`\`js +> ugly ( code ) ; +> \`\`\` +> \`\`\`\` + +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +> - test +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +\`\`\`\`md +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` +\`\`\`\` + +> \`\`\`\`md +> > \`\`\`md +> > +> > - This is a long long +> > long long long long +> > long long paragraph. +> > \`\`\` +> \`\`\`\` + +> +> +> - This is a long long +> long long long long +> long long paragraph. +> + +> \`\`\`\`js +> // prettier-ignore +> const x = 1, +> b = 2 +> \`\`\`\` + +=====================================output===================================== +> \`\`\`\`md +> +> \`\`\`js +> ugly ( code ) ; +> \`\`\` +> \`\`\`\` + +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +> - test +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` + +\`\`\`\`md +> \`\`\`md +> +> - This is a long long +> long long long long +> long long paragraph. +> \`\`\` +\`\`\`\` + +> \`\`\`\`md +> > \`\`\`md +> > +> > - This is a long long +> > long long long long +> > long long paragraph. +> > \`\`\` +> \`\`\`\` + +> +> - This is a long long +> long long long long +> long long paragraph. + +> \`\`\`js +> // prettier-ignore +> const x = 1, +> b = 2 +> \`\`\` + +================================================================================ +`; + exports[`nested.md - {"proseWrap":"always"} format 1`] = ` ====================================options===================================== parsers: ["markdown"] @@ -173,6 +487,185 @@ proseWrap: "always" ================================================================================ `; +exports[`nested.md - {"proseWrap":"never"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "never" + | printWidth +=====================================input====================================== +>>> 123 + +=====================================output===================================== +> > > 123 + +================================================================================ +`; + +exports[`nested.md - {"proseWrap":"preserve"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "preserve" + | printWidth +=====================================input====================================== +>>> 123 + +=====================================output===================================== +> > > 123 + +================================================================================ +`; + +exports[`notext-end.md - {"proseWrap":"always"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "always" + | printWidth +=====================================input====================================== +> [!NOTE] +> \`DOOM\` + +> _b_ +>> \`A\` +>> \`B\` + +> *a* +>> # foo +>> \`a\` > \`b\` + + +> This is a quote with an italic _across multuple lines +> which should just work_. So make sure there is no > if we set +> proseWrap to \`never\` + +> This is a quote with a link [across multuple lines +> which should just work](). So make sure there is no > if we set +> proseWrap to \`never\` + +=====================================output===================================== +> [!NOTE] \`DOOM\` + +> _b_ +> +> > \`A\` \`B\` + +> _a_ +> +> > # foo +> > +> > \`a\` > \`b\` + +> This is a quote with an italic _across multuple lines which should just work_. +> So make sure there is no > if we set proseWrap to \`never\` + +> This is a quote with a link [across multuple lines which should just work](). +> So make sure there is no > if we set proseWrap to \`never\` + +================================================================================ +`; + +exports[`notext-end.md - {"proseWrap":"never"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "never" + | printWidth +=====================================input====================================== +> [!NOTE] +> \`DOOM\` + +> _b_ +>> \`A\` +>> \`B\` + +> *a* +>> # foo +>> \`a\` > \`b\` + + +> This is a quote with an italic _across multuple lines +> which should just work_. So make sure there is no > if we set +> proseWrap to \`never\` + +> This is a quote with a link [across multuple lines +> which should just work](). So make sure there is no > if we set +> proseWrap to \`never\` + +=====================================output===================================== +> [!NOTE] \`DOOM\` + +> _b_ +> +> > \`A\` \`B\` + +> _a_ +> +> > # foo +> > +> > \`a\` > \`b\` + +> This is a quote with an italic _across multuple lines which should just work_. So make sure there is no > if we set proseWrap to \`never\` + +> This is a quote with a link [across multuple lines which should just work](). So make sure there is no > if we set proseWrap to \`never\` + +================================================================================ +`; + +exports[`notext-end.md - {"proseWrap":"preserve"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "preserve" + | printWidth +=====================================input====================================== +> [!NOTE] +> \`DOOM\` + +> _b_ +>> \`A\` +>> \`B\` + +> *a* +>> # foo +>> \`a\` > \`b\` + + +> This is a quote with an italic _across multuple lines +> which should just work_. So make sure there is no > if we set +> proseWrap to \`never\` + +> This is a quote with a link [across multuple lines +> which should just work](). So make sure there is no > if we set +> proseWrap to \`never\` + +=====================================output===================================== +> [!NOTE] +> \`DOOM\` + +> _b_ +> +> > \`A\` +> > \`B\` + +> _a_ +> +> > # foo +> > +> > \`a\` > \`b\` + +> This is a quote with an italic _across multuple lines +> which should just work_. So make sure there is no > if we set +> proseWrap to \`never\` + +> This is a quote with a link [across multuple lines +> which should just work](). So make sure there is no > if we set +> proseWrap to \`never\` + +================================================================================ +`; + exports[`paragraph.md - {"proseWrap":"always"} format 1`] = ` ====================================options===================================== parsers: ["markdown"] @@ -191,6 +684,39 @@ proseWrap: "always" ================================================================================ `; +exports[`paragraph.md - {"proseWrap":"never"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "never" + | printWidth +=====================================input====================================== +> This is a long long long long long long long long long long long long long long long paragraph. +> This is a long long long long long long long long long long long long long long long paragraph. + +=====================================output===================================== +> This is a long long long long long long long long long long long long long long long paragraph. This is a long long long long long long long long long long long long long long long paragraph. + +================================================================================ +`; + +exports[`paragraph.md - {"proseWrap":"preserve"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "preserve" + | printWidth +=====================================input====================================== +> This is a long long long long long long long long long long long long long long long paragraph. +> This is a long long long long long long long long long long long long long long long paragraph. + +=====================================output===================================== +> This is a long long long long long long long long long long long long long long long paragraph. +> This is a long long long long long long long long long long long long long long long paragraph. + +================================================================================ +`; + exports[`simple.md - {"proseWrap":"always"} format 1`] = ` ====================================options===================================== parsers: ["markdown"] @@ -205,3 +731,33 @@ proseWrap: "always" ================================================================================ `; + +exports[`simple.md - {"proseWrap":"never"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "never" + | printWidth +=====================================input====================================== +> 123 + +=====================================output===================================== +> 123 + +================================================================================ +`; + +exports[`simple.md - {"proseWrap":"preserve"} format 1`] = ` +====================================options===================================== +parsers: ["markdown"] +printWidth: 80 +proseWrap: "preserve" + | printWidth +=====================================input====================================== +> 123 + +=====================================output===================================== +> 123 + +================================================================================ +`; diff --git a/tests/format/markdown/blockquote/format.test.js b/tests/format/markdown/blockquote/format.test.js index 6ecc0af19..02d9abea4 100644 --- a/tests/format/markdown/blockquote/format.test.js +++ b/tests/format/markdown/blockquote/format.test.js @@ -1 +1,3 @@ runFormatTest(import.meta, ["markdown"], { proseWrap: "always" }); +runFormatTest(import.meta, ["markdown"], { proseWrap: "preserve" }); +runFormatTest(import.meta, ["markdown"], { proseWrap: "never" }); diff --git a/tests/format/markdown/blockquote/notext-end.md b/tests/format/markdown/blockquote/notext-end.md new file mode 100644 index 000000000..97435445a --- /dev/null +++ b/tests/format/markdown/blockquote/notext-end.md @@ -0,0 +1,19 @@ +> [!NOTE] +> `DOOM` + +> _b_ +>> `A` +>> `B` + +> *a* +>> # foo +>> `a` > `b` + + +> This is a quote with an italic _across multuple lines +> which should just work_. So make sure there is no > if we set +> proseWrap to `never` + +> This is a quote with a link [across multuple lines +> which should just work](). So make sure there is no > if we set +> proseWrap to `never`