Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 83 lines (69 sloc) 2.16 kb
066d63c @sstephenson Use mdtoc for the readme
authored
1 #!/usr/bin/env ruby
2
3 # A little Markdown filter that scans your document for headings,
4 # numbers them, adds anchors, and inserts a table of contents.
5 #
6 # To use it, make sure the headings you want numbered and linked are
7 # in this format:
8 #
9 # ### Title ###
10 #
11 # I.e. they must have an equal number of octothorpes around the title
12 # text. (In Markdown, `#` means `h1`, `##` means `h2`, and so on.)
13 # The table of contents will be inserted before the first such
14 # heading.
15 #
16 # Released into the public domain.
17 # Sam Stephenson <sstephenson@gmail.com>
18 # 2011-04-30
19
20 def mdtoc(markdown)
21 titles = []
22 lines = markdown.split($/)
23 start = nil
24
25 # First pass: Scan the Markdown source looking for titles of the
26 # format: `### Title ###`. Record the line number, header level
27 # (number of octothorpes), and text of each matching title.
28 lines.each_with_index do |line, line_no|
29 if line.match(/^(\#{1,6})\s+(.+?)\s+\1$/)
30 titles << [line_no, $1.length, $2]
31 start ||= line_no
32 end
33 end
34
35 last_section = nil
36 last_level = nil
37
38 # Second pass: Iterate over all matched titles and compute their
39 # corresponding section numbers. Then replace the titles with
40 # annotated anchors.
41 titles.each do |title_info|
42 line_no, level, text = title_info
43
44 if last_section
45 section = last_section.dup
46
47 if last_level < level
48 section << 1
49 else
50 (last_level - level).times { section.pop }
51 section[-1] += 1
52 end
53 else
54 section = [1]
55 end
56
57 name = section.join(".")
6c468d6 @sstephenson Don't highlight the anchors
authored
58 lines[line_no] = %(#{"#" * level} <a name="section_#{name}"></a> #{name} #{text})
066d63c @sstephenson Use mdtoc for the readme
authored
59
60 title_info << section
61 last_section = section
62 last_level = level
63 end
64
65 # Third pass: Iterate over matched titles once more to produce the
66 # table of contents. Then insert it immediately above the first
67 # matched title.
68 if start
69 toc = titles.map do |(line_no, level, text, section)|
70 name = section.join(".")
71 %(#{" " * (section.length * 3)}* [#{name} #{text}](#section_#{name}))
72 end + [""]
73
74 lines.insert(start, *toc)
75 end
76
77 lines.join("\n")
78 end
79
80 if __FILE__ == $0
81 puts mdtoc($<.read)
82 end
Something went wrong with that request. Please try again.