Skip to content

Commit

Permalink
Fix duplicate heading id
Browse files Browse the repository at this point in the history
  • Loading branch information
styfle committed Dec 20, 2018
1 parent 27145cc commit 08d9632
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 27 deletions.
37 changes: 34 additions & 3 deletions lib/marked.js
Original file line number Diff line number Diff line change
Expand Up @@ -953,13 +953,13 @@ Renderer.prototype.html = function(html) {
return html;
};

Renderer.prototype.heading = function(text, level, raw) {
Renderer.prototype.heading = function(text, level, raw, slugger) {
if (this.options.headerIds) {
return '<h'
+ level
+ ' id="'
+ this.options.headerPrefix
+ raw.toLowerCase().replace(/[^\w]+/g, '-')
+ slugger.slug(raw)
+ '">'
+ text
+ '</h'
Expand Down Expand Up @@ -1108,6 +1108,7 @@ function Parser(options) {
this.options.renderer = this.options.renderer || new Renderer();
this.renderer = this.options.renderer;
this.renderer.options = this.options;
this.slugger = new Slugger();
}

/**
Expand Down Expand Up @@ -1186,7 +1187,8 @@ Parser.prototype.tok = function() {
return this.renderer.heading(
this.inline.output(this.token.text),
this.token.depth,
unescape(this.inlineText.output(this.token.text)));
unescape(this.inlineText.output(this.token.text)),
this.slugger);
}
case 'code': {
return this.renderer.code(this.token.text,
Expand Down Expand Up @@ -1283,6 +1285,35 @@ Parser.prototype.tok = function() {
}
};

/**
* Slugger generates header id
*/

function Slugger () {
this.seen = {};
}

/**
* Convert string to unique id
*/

Slugger.prototype.slug = function (value) {
var slug = value
.toLowerCase()
.trim()
.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
.replace(/\s/g, '-');

var count = this.seen.hasOwnProperty(slug) ? this.seen[slug] + 1 : 0;
this.seen[slug] = count;

if (count > 0) {
slug = slug + '-' + count;
}

return slug;
};

/**
* Helpers
*/
Expand Down
8 changes: 4 additions & 4 deletions test/new/cm_blockquotes.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ <h3 id="example-192">Example 192</h3>
<p>The spaces after the <code>&gt;</code> characters can be omitted:</p>

<blockquote>
<h1 id="foo">Foo</h1>
<h1 id="bar">Bar</h1>
<p>bar
baz</p>
</blockquote>
Expand All @@ -21,7 +21,7 @@ <h3 id="example-193">Example 193</h3>
<p>The <code>&gt;</code> characters can be indented 1-3 spaces:</p>

<blockquote>
<h1 id="foo">Foo</h1>
<h1 id="baz">Baz</h1>
<p>bar
baz</p>
</blockquote>
Expand All @@ -30,7 +30,7 @@ <h3 id="example-194">Example 194</h3>

<p>Four spaces gives us a code block:</p>

<pre><code>&gt; # Foo
<pre><code>&gt; # Qux
&gt; bar
&gt; baz</code></pre>

Expand All @@ -39,7 +39,7 @@ <h3 id="example-195">Example 195</h3>
<p>The Laziness clause allows us to omit the <code>&gt;</code> before paragraph continuation text:</p>

<blockquote>
<h1 id="foo">Foo</h1>
<h1 id="quux">Quux</h1>
<p>bar
baz</p>
</blockquote>
Expand Down
8 changes: 4 additions & 4 deletions test/new/cm_blockquotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,31 @@

The spaces after the `>` characters can be omitted:

># Foo
># Bar
>bar
> baz
### Example 193

The `>` characters can be indented 1-3 spaces:

> # Foo
> # Baz
> bar
> baz
### Example 194

Four spaces gives us a code block:

> # Foo
> # Qux
> bar
> baz

### Example 195

The Laziness clause allows us to omit the `>` before paragraph continuation text:

> # Foo
> # Quux
> bar
baz

Expand Down
2 changes: 1 addition & 1 deletion test/new/toplevel_paragraphs.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<h1 id="how-are-you">how are you</h1>

<p>paragraph before head with equals</p>
<h1 id="how-are-you">how are you</h1>
<h1 id="how-are-you-again">how are you again</h1>

<p>paragraph before blockquote</p>
<blockquote><p>text for blockquote</p></blockquote>
Expand Down
2 changes: 1 addition & 1 deletion test/new/toplevel_paragraphs.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ paragraph before head with hash
# how are you

paragraph before head with equals
how are you
how are you again
===========

paragraph before blockquote
Expand Down
16 changes: 8 additions & 8 deletions test/original/markdown_documentation_basics.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1>Markdown: Basics</h1>
<h1 id="markdown-basics">Markdown: Basics</h1>

<ul id="ProjectSubmenu">
<li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
Expand All @@ -8,7 +8,7 @@ <h1>Markdown: Basics</h1>
<li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
</ul>

<h2>Getting the Gist of Markdown's Formatting Syntax</h2>
<h2 id="getting-the-gist-of-markdowns-formatting-syntax">Getting the Gist of Markdown's Formatting Syntax</h2>

<p>This page offers a brief overview of what it's like to use Markdown.
The <a href="/projects/markdown/syntax" title="Markdown Syntax">syntax page</a> provides complete, detailed documentation for
Expand All @@ -24,7 +24,7 @@ <h2>Getting the Gist of Markdown's Formatting Syntax</h2>
<p><strong>Note:</strong> This document is itself written using Markdown; you
can <a href="/projects/markdown/basics.text">see the source for it by adding '.text' to the URL</a>.</p>

<h2>Paragraphs, Headers, Blockquotes</h2>
<h2 id="paragraphs-headers-blockquotes">Paragraphs, Headers, Blockquotes</h2>

<p>A paragraph is simply one or more consecutive lines of text, separated
by one or more blank lines. (A blank line is any line that looks like a
Expand Down Expand Up @@ -88,7 +88,7 @@ <h2>Paragraphs, Headers, Blockquotes</h2>
&lt;/blockquote&gt;
</code></pre>

<h3>Phrase Emphasis</h3>
<h3 id="phrase-emphasis">Phrase Emphasis</h3>

<p>Markdown uses asterisks and underscores to indicate spans of emphasis.</p>

Expand All @@ -110,7 +110,7 @@ <h3>Phrase Emphasis</h3>
Or, if you prefer, &lt;strong&gt;use two underscores instead&lt;/strong&gt;.&lt;/p&gt;
</code></pre>

<h2>Lists</h2>
<h2 id="lists">Lists</h2>

<p>Unordered (bulleted) lists use asterisks, pluses, and hyphens (<code>*</code>,
<code>+</code>, and <code>-</code>) as list markers. These three markers are
Expand Down Expand Up @@ -181,7 +181,7 @@ <h2>Lists</h2>
&lt;/ul&gt;
</code></pre>

<h3>Links</h3>
<h3 id="links">Links</h3>

<p>Markdown supports two styles for creating links: <em>inline</em> and
<em>reference</em>. With both styles, you use square brackets to delimit the
Expand Down Expand Up @@ -244,7 +244,7 @@ <h3>Links</h3>
&lt;a href="http://www.nytimes.com/"&gt;The New York Times&lt;/a&gt;.&lt;/p&gt;
</code></pre>

<h3>Images</h3>
<h3 id="images">Images</h3>

<p>Image syntax is very much like link syntax.</p>

Expand All @@ -265,7 +265,7 @@ <h3>Images</h3>
<pre><code>&lt;img src="/path/to/img.jpg" alt="alt text" title="Title" /&gt;
</code></pre>

<h3>Code</h3>
<h3 id="code">Code</h3>

<p>In a regular paragraph, you can create code span by wrapping text in
backtick quotes. Any ampersands (<code>&amp;</code>) and angle brackets (<code>&lt;</code> or
Expand Down
26 changes: 20 additions & 6 deletions test/unit/marked-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,29 @@ var marked = require('../../lib/marked.js');

describe('Test heading ID functionality', function() {
it('should add id attribute by default', function() {
var renderer = new marked.Renderer(marked.defaults);
var header = renderer.heading('test', 1, 'test');
expect(header).toBe('<h1 id="test">test</h1>\n');
var html = marked('# test');
expect(html).toBe('<h1 id="test">test</h1>\n');
});

it('should add unique id for repeating heading 1280', function() {
var html = marked('# test\n# test\n# test');
expect(html).toBe('<h1 id="test">test</h1>\n<h1 id="test-1">test</h1>\n<h1 id="test-2">test</h1>\n');
});

it('should add id with non-latin chars', function() {
var html = marked('# привет');
expect(html).toBe('<h1 id="привет">привет</h1>\n');
});

it('should add id without ampersands 857', function() {
var html = marked('# This & That Section');
expect(html).toBe('<h1 id="this--that-section">This &amp; That Section</h1>\n');
});

it('should NOT add id attribute when options set false', function() {
var renderer = new marked.Renderer({ headerIds: false });
var header = renderer.heading('test', 1, 'test');
expect(header).toBe('<h1>test</h1>\n');
var options = { headerIds: false };
var html = marked('# test', options);
expect(html).toBe('<h1>test</h1>\n');
});
});

Expand Down

0 comments on commit 08d9632

Please sign in to comment.