// Simple typographic replacements // // (tm) (TM) → ™ // +- → ± // ... → … (also ?.... → ?.., !.... → !..) // ???????? → ???, !!!!! → !!!, `,,` → `,` // -- → –, --- → — // --> <-- -> <- to → ← → ← // (pa) (PA) → ¶ // // Disabled replacements: // // (c) (C) → © // (r) (R) → ® // (p) (P) -> § let RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--|-->|<--|->|<-/; let SCOPED_ABBR_RE = /\((tm|pa)\)/gi; let SCOPED_ABBR = { pa: "¶", tm: "™", }; function replaceFn(match, name) { return SCOPED_ABBR[name.toLowerCase()]; } function replaceScoped(inlineTokens) { let i, token; for (i = inlineTokens.length - 1; i >= 0; i--) { token = inlineTokens[i]; if (token.type === "text") { token.content = token.content.replace(SCOPED_ABBR_RE, replaceFn); } } } function replaceRare(inlineTokens) { let i, token, inside_autolink = 0; for (i = inlineTokens.length - 1; i >= 0; i--) { token = inlineTokens[i]; if (token.type === "text" && !inside_autolink) { if (RARE_RE.test(token.content)) { token.content = token.content .replace(/\+-/g, "±") // Custom arrows .replace(/(^|\s)-{1,2}>(\s|$)/gm, "\u0020\u2192\u0020") .replace(/(^|\s)<-{1,2}(\s|$)/gm, "\u0020\u2190\u0020") // .., ..., ....... -> … // but ?..... & !..... -> ?.. & !.. .replace(/\.{2,}/g, "…") .replace(/([?!])…/g, "$1..") .replace(/([?!]){4,}/g, "$1$1$1") .replace(/,{2,}/g, ",") // em-dash .replace(/(^|[^-])---(?=[^-]|$)/gm, "$1\u2014") // en-dash .replace(/(^|\s)--(?=\s|$)/gm, "$1\u2013") .replace(/(^|[^-\s])--(?=[^-\s]|$)/gm, "$1\u2013"); } } if (token.type === "link_open" && token.info === "auto") { inside_autolink--; } if (token.type === "link_close" && token.info === "auto") { inside_autolink++; } } } function replace(state) { let blkIdx; for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { if (state.tokens[blkIdx].type !== "inline") { continue; } if (SCOPED_ABBR_RE.test(state.tokens[blkIdx].content)) { replaceScoped(state.tokens[blkIdx].children); } if (RARE_RE.test(state.tokens[blkIdx].content)) { replaceRare(state.tokens[blkIdx].children); } } } export function setup(helper) { helper.registerPlugin((md) => { if (md.options.typographer) { md.core.ruler.at("replacements", replace); } }); }