Toolkit

:zap: A collection of helpful web app components

View project on GitHub
#!/usr/bin/env bash

################################
# This script can be used to read a markdown file and generate a table of contents using Github.
#
# USAGE: generate_table_of_contents [file]
#
# Place <!--ts--> where you want the table of contents to start.
# Place <!--te--> where you want the table of contents to end.
#
################################

# Converts local md file into html by GitHub
gh_toc_md2html() {
  local gh_file_md=$1
  URL=https://api.github.com/markdown/raw
  OUTPUT="$(curl -s --data-binary @"$gh_file_md" -H "Content-Type:text/plain" $URL)"
  if [ "$?" != "0" ]; then
    echo "XXNetworkErrorXX"
  fi
  if [ "$(echo "${OUTPUT}" | awk '/API rate limit exceeded/')" != "" ]; then
    echo "XXRateLimitXX"
  else
    echo "${OUTPUT}"
  fi
}

# TOC generator
gh_toc(){
  local gh_src=$1
  local gh_src_copy=$1
  local gh_ttl_docs=$2

  if [ "$gh_src" = "" ]; then
    echo "Please, enter URL or local path for a README.md"
    return
  fi

  local rawhtml=$(gh_toc_md2html "$gh_src")

  if [ "$rawhtml" == "XXNetworkErrorXX" ]; then
    echo "Parsing local markdown file requires access to github API"
    echo "Please make sure curl is installed and check your network connectivity"
    return
  fi

  if [ "$rawhtml" == "XXRateLimitXX" ]; then
    echo "Parsing local markdown file requires access to github API"
    echo "Error: You exceeded the hourly limit. See: https://developer.github.com/v3/#rate-limiting"
    return
  fi

  local toc=`echo "$rawhtml" | gh_toc_grab "$gh_src_copy"`

  if grep -Fxq "<!--ts-->" $gh_src && grep -Fxq "<!--te-->" $gh_src; then
   :
  else
    echo "You don't have <!--ts--> or <!--te--> in your file...exiting"
    return
  fi

  local ts="<\!--ts-->"
  local te="<\!--te-->"
  local dt=`date +'%F_%H%M%S'`
  local ext=".orig.${dt}"
  local toc_path="${gh_src}.toc.${dt}"

  sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" # clear old TOC
  echo "${toc}" > "${toc_path}" # create toc file
  if [[ "`uname`" == "Darwin" ]]; then # insert toc file
    sed -i "" "/${ts}/r ${toc_path}" "$gh_src"
  else
    sed -i "/${ts}/r ${toc_path}" "$gh_src"
  fi

  rm "${gh_src}${ext}"
  rm "${toc_path}"
}

# Gets the TOC from the rendered html
gh_toc_grab() {
  # if closed <h[1-6]> is on the new line, then move it on the prev line
  # for example:
  #   was: The command <code>foo1</code>
  #      </h1>
  #   became: The command <code>foo1</code></h1>
  sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' |
  grep -E -o '<a.*id="user-content-[^"]*".*</h[2-6]' | # find strings that corresponds to template
  sed 's/<code>//g' | sed 's/<\/code>//g' | # remove code tags
    # now all rows are like:
  #   <a id="user-content-..." href="..."><span ...></span></a> ... </h1
  # format result line
  #   * $0 — whole string
  #   * last element of each row: "</hN" where N in (1,2,3,...)
  echo "$(awk -v "gh_url=$1" '{
  level = substr($0, length($0), 1)-2
  text = substr($0, match($0, /a>.*<\/h/)+2, RLENGTH-5)
  href = substr($0, match($0, "href=\"[^\"]+?\"")+6, RLENGTH-7)
  print sprintf("%*s", level*2, "") "* [" text "](" href ")" }' |
    sed 'y/+/ /; s/%/\\x/g')"
}

main() {
for md in "$@"
do
  gh_toc "$md" "$#"
done
}
main "$@"