Skip to content

Commit 86b560c

Browse files
committed
- Fix inline Table of Contents invalid parsing of code blocks
1 parent 540020d commit 86b560c

7 files changed

Lines changed: 111 additions & 23 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
module Madness
2+
# Generate a markdown Table of Contents for a single document
3+
class InlineTableOfContents
4+
include ServerHelper
5+
using StringRefinements
6+
7+
attr_reader :text
8+
9+
def initialize(text)
10+
@text = text
11+
end
12+
13+
def markdown
14+
@markdown ||= markdown!
15+
end
16+
17+
protected
18+
19+
def markdown!
20+
result = ["#{caption}\n"]
21+
text.lines(chomp: true).each do |line|
22+
next unless line.start_with?('#', '```')
23+
next if inside_code_block? line
24+
25+
matches = line.match(/^(?<level>\#{2,3})\s+(?<text>.+)/)
26+
next unless matches
27+
28+
level = matches[:level].size - 2
29+
text = matches[:text]
30+
31+
spacer = ' ' * level
32+
result.push "#{spacer}- [#{text}](##{text.to_slug})"
33+
end
34+
35+
result.join "\n"
36+
end
37+
38+
def caption
39+
@caption ||= config.auto_toc.is_a?(String) ? config.auto_toc : '## Table of Contents'
40+
end
41+
42+
def inside_code_block?(line)
43+
@marker ||= false
44+
45+
if !@marker && line.start_with?('```')
46+
@marker = line[/^`{3,4}/]
47+
elsif @marker && line.start_with?(@marker)
48+
@marker = false
49+
end
50+
51+
!!@marker
52+
end
53+
end
54+
end

lib/madness/markdown_document.rb

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -74,30 +74,12 @@ def redcarpet_handler
7474
config.highlighter ? HighlightRenderer : Redcarpet::Render::HTML
7575
end
7676

77-
def toc_caption
78-
@toc_caption ||= if config.auto_toc.is_a?(String)
79-
config.auto_toc
80-
else
81-
'## Table of Contents'
82-
end
83-
end
84-
8577
def toc
86-
result = ["#{toc_caption}\n"]
87-
markdown.lines(chomp: true).each do |line|
88-
next unless line.start_with? '#'
89-
90-
matches = line.match(/^(?<level>\#{2,3})\s+(?<text>.+)/)
91-
next unless matches
92-
93-
level = matches[:level].size - 1
94-
text = matches[:text]
95-
96-
spacer = ' ' * level
97-
result.push "#{spacer}- [#{text}](##{text.to_slug})"
98-
end
78+
@toc ||= toc_handler.markdown
79+
end
9980

100-
result.join "\n"
81+
def toc_handler
82+
@toc_handler ||= Madness::InlineTableOfContents.new markdown
10183
end
10284
end
10385
end

lib/madness/table_of_contents.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Madness
2-
# Generate a markdown Table of Contents
2+
# Generate a markdown Table of Contents for the entire site
33
class TableOfContents
44
include ServerHelper
55

spec/approvals/inline-toc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Table of Contents
2+
3+
- [Header 2](#header-2)
4+
- [Level 3 header](#level-3-header)
5+
- [Another Header 2](#another-header-2)

spec/approvals/toc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@
4040
1. [File with H1 alt](/File%20with%20H1%20alt)
4141
1. [File with Shortlink](/File%20with%20Shortlink)
4242
1. [File with Spaces](/File%20with%20Spaces)
43+
1. [File with TOC and Code](/File%20with%20TOC%20and%20Code)
4344
1. [File with TOC](/File%20with%20TOC)
4445
1. [File without H1](/File%20without%20H1)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# File with TOC and Code
2+
3+
<!-- TOC -->
4+
5+
This file ensures that headers in code blocks (including nested code blocks)
6+
are ignored in TOC generation
7+
8+
````
9+
10+
## Header trap (should not be included)
11+
12+
The below is displayed as is (unparsed)
13+
14+
```
15+
## Code block in code block (also a trap)
16+
```
17+
18+
````
19+
20+
```markdown
21+
## Header in code block with language identifier
22+
```
23+
24+
## Header 2
25+
26+
Some text
27+
28+
### Level 3 header
29+
30+
More text
31+
32+
## Another Header 2
33+
34+
Even more text
35+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
describe InlineTableOfContents do
2+
subject { described_class.new markdown }
3+
4+
let(:markdown) { File.read 'spec/fixtures/docroot/File with TOC and Code.md' }
5+
6+
describe '#markdown' do
7+
it 'returns a markdown table of contents' do
8+
expect(subject.markdown).to match_approval('inline-toc')
9+
end
10+
end
11+
end

0 commit comments

Comments
 (0)