pyenv/doc/mdtoc.rb
2011-08-11 14:21:15 -05:00

82 lines
2.1 KiB
Ruby

#!/usr/bin/env ruby
# A little Markdown filter that scans your document for headings,
# numbers them, adds anchors, and inserts a table of contents.
#
# To use it, make sure the headings you want numbered and linked are
# in this format:
#
# ### Title ###
#
# I.e. they must have an equal number of octothorpes around the title
# text. (In Markdown, `#` means `h1`, `##` means `h2`, and so on.)
# The table of contents will be inserted before the first such
# heading.
#
# Released into the public domain.
# Sam Stephenson <sstephenson@gmail.com>
# 2011-04-30
def mdtoc(markdown)
titles = []
lines = markdown.split($/)
start = nil
# First pass: Scan the Markdown source looking for titles of the
# format: `### Title ###`. Record the line number, header level
# (number of octothorpes), and text of each matching title.
lines.each_with_index do |line, line_no|
if line.match(/^(\#{1,6})\s+(.+?)\s+\1$/)
titles << [line_no, $1.length, $2]
start ||= line_no
end
end
last_section = nil
last_level = nil
# Second pass: Iterate over all matched titles and compute their
# corresponding section numbers. Then replace the titles with
# annotated anchors.
titles.each do |title_info|
line_no, level, text = title_info
if last_section
section = last_section.dup
if last_level < level
section << 1
else
(last_level - level).times { section.pop }
section[-1] += 1
end
else
section = [1]
end
name = section.join(".")
lines[line_no] = %(#{"#" * level} <a name="section_#{name}">#{name}</a> #{text})
title_info << section
last_section = section
last_level = level
end
# Third pass: Iterate over matched titles once more to produce the
# table of contents. Then insert it immediately above the first
# matched title.
if start
toc = titles.map do |(line_no, level, text, section)|
name = section.join(".")
%(#{" " * (section.length * 3)}* [#{name} #{text}](#section_#{name}))
end + [""]
lines.insert(start, *toc)
end
lines.join("\n")
end
if __FILE__ == $0
puts mdtoc($<.read)
end