FIX: Generate unique HTML heading names ()

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
app/assets/javascripts
discourse/tests/unit/lib
pretty-text/engines/discourse-markdown
plugins/poll/spec/lib
spec/components

@ -670,7 +670,7 @@ eviltrout</p>
assert.cooked( assert.cooked(
"# #category-hashtag", "# #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" "it works within ATX-style headers"
); );
@ -696,7 +696,7 @@ eviltrout</p>
test("Heading", function (assert) { test("Heading", function (assert) {
assert.cooked( assert.cooked(
"**Bold**\n----------", "**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" "It will bold the heading"
); );
}); });
@ -939,7 +939,7 @@ eviltrout</p>
assert.cooked( assert.cooked(
"## a\nb\n```\nc\n```", "## 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." "it handles headings with code blocks after them."
); );
}); });

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

@ -189,8 +189,8 @@ describe PrettyText do
</div> </div>
HTML 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=\"pre-heading-1\" class=\"anchor\" href=\"#pre-heading-1\"></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=\"post-heading-2\" class=\"anchor\" href=\"#post-heading-2\"></a>Post-heading</h1>")
end end
it "does not break when there are headings before/after a poll without a title" do 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"> <div class="poll" data-poll-status="open" data-poll-name="poll">
HTML 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=\"pre-heading-1\" class=\"anchor\" href=\"#pre-heading-1\"></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=\"post-heading-2\" class=\"anchor\" href=\"#post-heading-2\"></a>Post-heading</h1>")
end end
end end

@ -1909,7 +1909,7 @@ HTML
html = <<~HTML html = <<~HTML
<h1> <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 Hello world
</h1> </h1>
HTML HTML