FEATURE: Replace markdown-it replacements rule. (#12417)

We override the default replacements rule to no longer replace "(c)", "(p)", and "(p)". Additionally, we merged the custom arrows rule into the replacement function.
This commit is contained in:
Roman Rizzi 2021-03-18 10:55:41 -03:00 committed by GitHub
parent 1a433193d1
commit da210b6d77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 34 deletions

View File

@ -1601,4 +1601,74 @@ var bar = 'bar';
assert.cookedOptions(" -->asd ", enabledTypographer, "<p>&gt;asd</p>"); assert.cookedOptions(" -->asd ", enabledTypographer, "<p>&gt;asd</p>");
assert.cookedOptions(" -->asd", enabledTypographer, "<p>&gt;asd</p>"); assert.cookedOptions(" -->asd", enabledTypographer, "<p>&gt;asd</p>");
}); });
test("default typhographic replacements", function (assert) {
const enabledTypographer = {
siteSettings: { enable_markdown_typographer: true },
};
assert.cookedOptions("(bad)", enabledTypographer, "<p>(bad)</p>");
assert.cookedOptions("+-5", enabledTypographer, "<p>±5</p>");
assert.cookedOptions(
"test.. test... test..... test?..... test!....",
enabledTypographer,
"<p>test… test… test… test?.. test!..</p>"
);
assert.cookedOptions(
"!!!!!! ???? ,,",
enabledTypographer,
"<p>!!! ??? ,</p>"
);
assert.cookedOptions(
"!!!!!! ???? ,,",
enabledTypographer,
"<p>!!! ??? ,</p>"
);
assert.cookedOptions("(tm) (TM)", enabledTypographer, "<p>™ ™</p>");
});
test("default typhographic replacements - dashes", function (assert) {
const enabledTypographer = {
siteSettings: { enable_markdown_typographer: true },
};
assert.cookedOptions(
"---markdownit --- super---",
enabledTypographer,
"<p>—markdownit — super—</p>"
);
assert.cookedOptions(
"markdownit---awesome",
enabledTypographer,
"<p>markdownit—awesome</p>"
);
assert.cookedOptions("abc ----", enabledTypographer, "<p>abc ----</p>");
assert.cookedOptions(
"--markdownit -- super--",
enabledTypographer,
"<p>markdownit super</p>"
);
assert.cookedOptions(
"markdownit--awesome",
enabledTypographer,
"<p>markdownitawesome</p>"
);
assert.cookedOptions("1---2---3", enabledTypographer, "<p>1—2—3</p>");
assert.cookedOptions("1--2--3", enabledTypographer, "<p>123</p>");
assert.cookedOptions(
"<p>1 3</p>",
enabledTypographer,
"<p>1 3</p>"
);
});
test("disabled typhographic replacements", function (assert) {
const enabledTypographer = {
siteSettings: { enable_markdown_typographer: true },
};
assert.cookedOptions("(c) (C)", enabledTypographer, "<p>(c) (C)</p>");
assert.cookedOptions("(r) (R)", enabledTypographer, "<p>(r) (R)</p>");
assert.cookedOptions("(p) (P)", enabledTypographer, "<p>(p) (P)</p>");
});
}); });

View File

@ -0,0 +1,95 @@
// Simple typographic replacements
//
// (tm) (TM) → ™
// +- → ±
// ... → … (also ?.... → ?.., !.... → !..)
// ???????? → ???, !!!!! → !!!, `,,` → `,`
// -- → &ndash;, --- → &mdash;
// --> <-- -> <- to → ← → ←
//
// Disabled replacements:
//
// (c) (C) → ©
// (r) (R) → ®
// (p) (P) -> §
let RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--|-->|<--|->|<-/;
let SCOPED_ABBR_RE = /\((tm)\)/gi;
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, () => {
return "™";
});
}
}
}
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);
}
});
}

View File

@ -1,32 +0,0 @@
function replaceArrows(state) {
for (let i = 0; i < state.tokens.length; i++) {
let token = state.tokens[i];
if (token.type !== "inline") {
continue;
}
const arrowsRegexp = /-->|<--|->|<-/;
if (arrowsRegexp.test(token.content)) {
for (let ci = 0; ci < token.children.length; ci++) {
let child = token.children[ci];
if (child.type === "text") {
if (arrowsRegexp.test(child.content)) {
child.content = child.content
.replace(/(^|\s)-{1,2}>(\s|$)/gm, "\u0020\u2192\u0020")
.replace(/(^|\s)<-{1,2}(\s|$)/gm, "\u0020\u2190\u0020");
}
}
}
}
}
}
export function setup(helper) {
helper.registerPlugin((md) => {
if (md.options.typographer) {
md.core.ruler.before("replacements", "typographer-arrow", replaceArrows);
}
});
}

View File

@ -1442,10 +1442,10 @@ HTML
it 'supports typographer' do it 'supports typographer' do
SiteSetting.enable_markdown_typographer = true SiteSetting.enable_markdown_typographer = true
expect(PrettyText.cook('(tm)')).to eq('<p>™</p>') expect(PrettyText.cook('->')).to eq('<p> → </p>')
SiteSetting.enable_markdown_typographer = false SiteSetting.enable_markdown_typographer = false
expect(PrettyText.cook('(tm)')).to eq('<p>(tm)</p>') expect(PrettyText.cook('->')).to eq('<p>-&gt;</p>')
end end
it 'uses quotation marks from site settings' do it 'uses quotation marks from site settings' do