2021-03-18 09:55:41 -04:00
|
|
|
// Simple typographic replacements
|
|
|
|
//
|
|
|
|
// (tm) (TM) → ™
|
|
|
|
// +- → ±
|
|
|
|
// ... → … (also ?.... → ?.., !.... → !..)
|
|
|
|
// ???????? → ???, !!!!! → !!!, `,,` → `,`
|
|
|
|
// -- → –, --- → —
|
2023-02-14 14:24:14 -05:00
|
|
|
// --> <-- -> <- <-> <--> to → ← → ← ↔ ↔
|
2021-03-22 14:04:08 -04:00
|
|
|
// (pa) (PA) → ¶
|
2021-03-18 09:55:41 -04:00
|
|
|
//
|
|
|
|
// Disabled replacements:
|
|
|
|
//
|
|
|
|
// (c) (C) → ©
|
|
|
|
// (r) (R) → ®
|
|
|
|
// (p) (P) -> §
|
|
|
|
|
2023-02-14 14:24:14 -05:00
|
|
|
let RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--|-->|<--|->|<-|<->|<-->/;
|
2021-03-22 14:04:08 -04:00
|
|
|
|
|
|
|
let SCOPED_ABBR_RE = /\((tm|pa)\)/gi;
|
|
|
|
let SCOPED_ABBR = {
|
|
|
|
pa: "¶",
|
|
|
|
tm: "™",
|
|
|
|
};
|
|
|
|
|
|
|
|
function replaceFn(match, name) {
|
|
|
|
return SCOPED_ABBR[name.toLowerCase()];
|
|
|
|
}
|
2021-03-18 09:55:41 -04:00
|
|
|
|
|
|
|
function replaceScoped(inlineTokens) {
|
|
|
|
let i, token;
|
|
|
|
|
|
|
|
for (i = inlineTokens.length - 1; i >= 0; i--) {
|
|
|
|
token = inlineTokens[i];
|
|
|
|
if (token.type === "text") {
|
2021-03-22 14:04:08 -04:00
|
|
|
token.content = token.content.replace(SCOPED_ABBR_RE, replaceFn);
|
2021-03-18 09:55:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
2023-02-14 14:24:14 -05:00
|
|
|
.replace(/(^|\s)<-{1,2}>(\s|$)/gm, "\u0020\u2194\u0020")
|
2021-03-18 09:55:41 -04:00
|
|
|
// .., ..., ....... -> …
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|