FIX: Generate unique HTML heading names (#12705)

Headings with the exact same name generated exactly the same heading
names, which was invalid. This replaces the old code for generating
names for non-English headings which were using URI encode and resulted
in unreadable headings.
This commit is contained in:
Bianca Nenciu 2021-04-16 10:54:19 +03:00 committed by GitHub
parent 42f6c9b6b9
commit 96a16123d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 14 additions and 20 deletions

View File

@ -670,7 +670,7 @@ eviltrout</p>
assert.cooked(
"# #category-hashtag",
'<h1><a name="category-hashtag" class="anchor" href="#category-hashtag"></a><span class="hashtag">#category-hashtag</span></h1>',
'<h1><a name="category-hashtag-1" class="anchor" href="#category-hashtag-1"></a><span class="hashtag">#category-hashtag</span></h1>',
"it works within ATX-style headers"
);
@ -696,7 +696,7 @@ eviltrout</p>
test("Heading", function (assert) {
assert.cooked(
"**Bold**\n----------",
'<h2><a name="bold" class="anchor" href="#bold"></a><strong>Bold</strong></h2>',
'<h2><a name="bold-1" class="anchor" href="#bold-1"></a><strong>Bold</strong></h2>',
"It will bold the heading"
);
});
@ -939,7 +939,7 @@ eviltrout</p>
assert.cooked(
"## a\nb\n```\nc\n```",
'<h2><a name="a" class="anchor" href="#a"></a>a</h2>\n<p>b</p>\n<pre><code class="lang-auto">c\n</code></pre>',
'<h2><a name="a-1" class="anchor" href="#a-1"></a>a</h2>\n<p>b</p>\n<pre><code class="lang-auto">c\n</code></pre>',
"it handles headings with code blocks after them."
);
});

View File

@ -1,5 +1,3 @@
const SPECIAL_CHARACTERS_REGEX = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;
export function setup(helper) {
if (helper.getOptions().previewing) {
return;
@ -7,7 +5,11 @@ export function setup(helper) {
helper.registerPlugin((md) => {
md.core.ruler.push("anchor", (state) => {
for (let idx = 0, lvl = 0; idx < state.tokens.length; idx++) {
for (
let idx = 0, lvl = 0, headingId = 0;
idx < state.tokens.length;
idx++
) {
if (
state.tokens[idx].type === "blockquote_open" ||
(state.tokens[idx].type === "bbcode_open" &&
@ -37,15 +39,7 @@ export function setup(helper) {
.replace(/^-+/, "")
.replace(/-+$/, "");
if (slug.length === 0) {
slug = state.tokens[idx + 1].content
.replace(/\s+/g, "-")
.replace(SPECIAL_CHARACTERS_REGEX, "")
.replace(/\-\-+/g, "-")
.replace(/^-+/, "")
.replace(/-+$/, "");
slug = encodeURI(slug).replace(/%/g, "").substr(0, 24);
}
slug = `${slug || "heading"}-${++headingId}`;
linkOpen.attrSet("name", slug);
linkOpen.attrSet("class", "anchor");

View File

@ -189,8 +189,8 @@ describe PrettyText do
</div>
HTML
expect(cooked).to include("<h1>\n<a name=\"pre-heading\" class=\"anchor\" href=\"#pre-heading\"></a>Pre-heading</h1>")
expect(cooked).to include("<h1>\n<a name=\"post-heading\" class=\"anchor\" href=\"#post-heading\"></a>Post-heading</h1>")
expect(cooked).to include("<h1>\n<a name=\"pre-heading-1\" class=\"anchor\" href=\"#pre-heading-1\"></a>Pre-heading</h1>")
expect(cooked).to include("<h1>\n<a name=\"post-heading-2\" class=\"anchor\" href=\"#post-heading-2\"></a>Post-heading</h1>")
end
it "does not break when there are headings before/after a poll without a title" do
@ -211,7 +211,7 @@ describe PrettyText do
<div class="poll" data-poll-status="open" data-poll-name="poll">
HTML
expect(cooked).to include("<h1>\n<a name=\"pre-heading\" class=\"anchor\" href=\"#pre-heading\"></a>Pre-heading</h1>")
expect(cooked).to include("<h1>\n<a name=\"post-heading\" class=\"anchor\" href=\"#post-heading\"></a>Post-heading</h1>")
expect(cooked).to include("<h1>\n<a name=\"pre-heading-1\" class=\"anchor\" href=\"#pre-heading-1\"></a>Pre-heading</h1>")
expect(cooked).to include("<h1>\n<a name=\"post-heading-2\" class=\"anchor\" href=\"#post-heading-2\"></a>Post-heading</h1>")
end
end

View File

@ -1909,7 +1909,7 @@ HTML
html = <<~HTML
<h1>
<a name="hello-world" class="anchor" href="#hello-world"></a>
<a name="hello-world-1" class="anchor" href="#hello-world-1"></a>
Hello world
</h1>
HTML