diff --git a/src/language-markdown/print/word.js b/src/language-markdown/print/word.js new file mode 100644 index 000000000..66f4d1c8f --- /dev/null +++ b/src/language-markdown/print/word.js @@ -0,0 +1,56 @@ +import { PUNCTUATION_REGEXP } from "../constants.evaluate.js"; +import { isAutolink } from "../utils.js"; + +/** + * @import AstPath from "../../common/ast-path.js" + * @import {Doc} from "../../document/index.js" + */ + +/** + * @param {AstPath} path + * @return {Doc} + */ +function printWord(path) { + const { node } = path; + let escapedValue = node.value + .replaceAll("*", String.raw`\*`) // escape all `*` + .replaceAll( + new RegExp( + [ + `(^|${PUNCTUATION_REGEXP.source})(_+)`, + `(_+)(${PUNCTUATION_REGEXP.source}|$)`, + ].join("|"), + "gu", + ), + (_, text1, underscore1, underscore2, text2) => + (underscore1 + ? `${text1}${underscore1}` + : `${underscore2}${text2}` + ).replaceAll("_", String.raw`\_`), + ); // escape all `_` except concating with non-punctuation, e.g. `1_2_3` is not considered emphasis + + const isFirstSentence = (node, name, index) => + node.type === "sentence" && index === 0; + const isLastChildAutolink = (node, name, index) => + isAutolink(node.children[index - 1]); + + if ( + escapedValue !== node.value && + (path.match(undefined, isFirstSentence, isLastChildAutolink) || + path.match( + undefined, + isFirstSentence, + (node, name, index) => node.type === "emphasis" && index === 0, + isLastChildAutolink, + )) + ) { + // backslash is parsed as part of autolinks, so we need to remove it + escapedValue = escapedValue.replace(/^(\\?[*_])+/u, (prefix) => + prefix.replaceAll("\\", ""), + ); + } + + return escapedValue; +} + +export { printWord }; diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index c0ea8f8ff..22a2a063e 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -19,7 +19,6 @@ import getMinNotPresentContinuousCount from "../utils/get-min-not-present-contin import getPreferredQuote from "../utils/get-preferred-quote.js"; import UnexpectedNodeError from "../utils/unexpected-node-error.js"; import clean from "./clean.js"; -import { PUNCTUATION_REGEXP } from "./constants.evaluate.js"; import embed from "./embed.js"; import getVisitorKeys from "./get-visitor-keys.js"; import { locEnd, locStart } from "./loc.js"; @@ -27,6 +26,7 @@ import { insertPragma } from "./pragma.js"; import { printChildren } from "./print/children.js"; import { printList } from "./print/list.js"; import { printTable } from "./print/table.js"; +import { printWord } from "./print/word.js"; import { printParagraph } from "./print-paragraph.js"; import preprocess from "./print-preprocess.js"; import { printSentence } from "./print-sentence.js"; @@ -99,47 +99,8 @@ function genericPrint(path, options, print) { return printParagraph(path, options, print); case "sentence": return printSentence(path, print); - case "word": { - let escapedValue = node.value - .replaceAll("*", String.raw`\*`) // escape all `*` - .replaceAll( - new RegExp( - [ - `(^|${PUNCTUATION_REGEXP.source})(_+)`, - `(_+)(${PUNCTUATION_REGEXP.source}|$)`, - ].join("|"), - "gu", - ), - (_, text1, underscore1, underscore2, text2) => - (underscore1 - ? `${text1}${underscore1}` - : `${underscore2}${text2}` - ).replaceAll("_", String.raw`\_`), - ); // escape all `_` except concating with non-punctuation, e.g. `1_2_3` is not considered emphasis - - const isFirstSentence = (node, name, index) => - node.type === "sentence" && index === 0; - const isLastChildAutolink = (node, name, index) => - isAutolink(node.children[index - 1]); - - if ( - escapedValue !== node.value && - (path.match(undefined, isFirstSentence, isLastChildAutolink) || - path.match( - undefined, - isFirstSentence, - (node, name, index) => node.type === "emphasis" && index === 0, - isLastChildAutolink, - )) - ) { - // backslash is parsed as part of autolinks, so we need to remove it - escapedValue = escapedValue.replace(/^(\\?[*_])+/u, (prefix) => - prefix.replaceAll("\\", ""), - ); - } - - return escapedValue; - } + case "word": + return printWord(path); case "whitespace": { const { next } = path;