Skip to content

Instantly share code, notes, and snippets.

@albertodebortoli
Last active April 7, 2022 14:14
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save albertodebortoli/9310424 to your computer and use it in GitHub Desktop.
Save albertodebortoli/9310424 to your computer and use it in GitHub Desktop.
Generate Markdown TOC
#!/usr/bin/env ruby
File.open("your_file.md", 'r') do |f|
f.each_line do |line|
forbidden_words = ['Table of contents', 'define', 'pragma']
next if !line.start_with?("#") || forbidden_words.any? { |w| line =~ /#{w}/ }
title = line.gsub("#", "").strip
href = title.gsub(" ", "-").downcase
puts " " * (line.count("#")-1) + "* [#{title}](\##{href})"
end
end
@jayelkaake
Copy link

Awesome - thanks!!

@lukesarnacki
Copy link

it was also capturing lines commented in code snippets, fix:

#!/usr/bin/env ruby

File.open("finaldocuments/README.md", 'r') do |f|
  inside_code_snippet = false
  f.each_line do |line|
    forbidden_words = ['Table of contents', 'define', 'pragma']
    inside_code_snippet = !inside_code_snippet if line.start_with?('```')
    next if !line.start_with?("#") || forbidden_words.any? { |w| line =~ /#{w}/ } || inside_code_snippet

    title = line.gsub("#", "").strip
    href = title.gsub(" ", "-").downcase
    puts "  " * (line.count("#")-1) + "* [#{title}](\##{href})"
  end
end

@con-f-use
Copy link

Has trouble with # inside codeblocks. Mistakes them for a heading.

# This is a heading
I'm a paragraph, dude!

## This is a heading one level below
I'm a hpargarap spelled backwards!
\```
echo "Some code"
# This must not a be heading
echo "s'more code"
\```

The text # This must not be a heading is mistaken for a heading.

@machta
Copy link

machta commented Aug 5, 2018

Thanks, very nice. Here is a version that takes the file name as an argument:

#!/usr/bin/env ruby

fileName = ARGV[0]
fileName = "README.md" if !fileName

File.open(fileName, 'r') do |f|
  inside_code_snippet = false
  f.each_line do |line|
    forbidden_words = ['Table of contents', 'define', 'pragma']
    inside_code_snippet = !inside_code_snippet if line.start_with?('```')
    next if !line.start_with?("#") || forbidden_words.any? { |w| line =~ /#{w}/ } || inside_code_snippet

    title = line.gsub("#", "").strip
    href = title.gsub(" ", "-").downcase
    puts "  " * (line.count("#")-1) + "* [#{title}](\##{href})"
  end
end

@shitana
Copy link

shitana commented Feb 17, 2019

I updated and included your script in this repo.
https://github.com/shitana/md_TOC_generator
(I also put the link to your script and profile)

@asantosca
Copy link

asantosca commented Feb 22, 2021

The script is not stripping invalid characters used as Headers. For example,

#### The field `b` means ("bug")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment