Markdown toc generator

Markdow 문서에서 목차를 만들어내는 프로그램

usage: toc.py [-h] [-d DEPTH] [-c] [-i INDENT] [-f FORMAT] filename

positional arguments:
  filename

optional arguments:
  -h, --help            show this help message and exit
  -d DEPTH, --depth DEPTH
                        Start section number
  -c, --clipboard       Copy output to clipboard
  -i INDENT, --indent INDENT
                        Indent size
  -f FORMAT, --format FORMAT
                        ex) DDD* - D:1. d:1.1. *:* a:a. A:A. H:가. h:ㄱ.

-d option은 무시할 subsection, -c option을 주면 내용이 clipboard에 복사된다. -i option은 section이 표시될 때 사용될 indent size이고, -f option은 제목의 format을 지정한다.

jmjeong$ ./toc.py mtest.md
1. A
    1. Aa
        * aa
        * ab
    2. Ab
2. B
    1. Ba
        * ba
        * bb
    2. Bb
    3. Bc
3. C
    1. Ca
        * ca
jmjeong$ ./toc.py -f ADh mtest.md
A. A
    1. Aa
        ㄱ. aa
        ㄴ. ab
    2. Ab
B. B
    1. Ba
        ㄱ. ba
        ㄴ. bb
    2. Bb
    3. Bc
C. C
    1. Ca
        ㄱ. ca
jmjeong$ ./toc.py -f Add mtest.md
A. A
    1.1. Aa
        1.1.1. aa
        1.1.2. ab
    1.2. Ab
B. B
    2.1. Ba
        2.1.1. ba
        2.1.2. bb
    2.2. Bb
    2.3. Bc
C. C
    3.1. Ca
        3.1.1. ca
jmjeong$ ./toc.py -f Hd* mtest.md
가. A
    1.1. Aa
        * aa
        * ab
    1.2. Ab
나. B
    2.1. Ba
        * ba
        * bb
    2.2. Bb
    2.3. Bc
다. C
    3.1. Ca
        * ca		

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# markdown toc generator, v1.8
#
# Jaemok Jeong, 2014/10/27

from AppKit import NSPasteboard, NSArray
import re
import argparse

headRe = re.compile(r"(#+)\s*([^#]*)")

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

def sendToClipboard(content):
    pb = NSPasteboard.generalPasteboard()
    pb.clearContents()
    c = NSArray.arrayWithObject_(content)
    pb.writeObjects_(c)


def generateSectionNum(format, depth, pos, sectionCount):
    # print format, depth, pos, sectionCount
    ganadara = u'가나다라마바사아자차카타파하'
    gndr = u'ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ'
    try:
        currentFormat = format[pos-depth]
    except IndexError:
        currentFormat = 'D'

    if currentFormat == 'd':
        return ".".join([str(s) for s in sectionCount if s != 0])[(depth-1)*2:]+"."
    elif currentFormat == '*':
        return "*"
    elif currentFormat == 'A':
        return chr(ord('A')+sectionCount[pos-1]-1)+"."
    elif currentFormat == 'a':
        return chr(ord('a')+sectionCount[pos-1]-1)+"."
    elif currentFormat == 'H':
        return ganadara[sectionCount[pos-1]-1]+"."
    elif currentFormat == 'h':
        return gndr[sectionCount[pos-1]-1]+"."
    else:
        return str(sectionCount[pos-1])+"."

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--depth", help="Start section number", type=int, default=2)
    parser.add_argument("-c", "--clipboard", help="Copy output to clipboard", dest='clipboard', action='store_true')
    parser.add_argument("-i", "--indent", help="Indent size", type=int, default=4)
    parser.add_argument("-f", "--format", help="ex) DDD* - D:1. d:1.1. *:* a:a. A:A. H:가. h:ㄱ.", default="DD**")
    parser.add_argument('filename')

    args = parser.parse_args()

    sectionCount = [0,0,0,0,0]
    currentPos = 0
    toc = ""

    with open(args.filename, "r") as f:
        for line in f.readlines():
            l = line.strip()
            if l.startswith("#"):
                m = headRe.match(l)
                c = len(m.group(1))
                if (c < args.depth):
                    continue
                if c == currentPos:
                    sectionCount[c-1] += 1
                elif c > currentPos:
                    for i in range(currentPos,c):
                        sectionCount[i] = 1
                    currentPos = c
                else:
                    sectionCount[c-1] += 1
                    for i in range(c,currentPos):
                        sectionCount[i] = 0
                    currentPos = c

                sec = generateSectionNum(args.format, args.depth, currentPos, sectionCount)
                spaces = " "*(currentPos-args.depth)*args.indent
                newsec = spaces + "%s %s\n" % (sec, m.group(2))
                print newsec.rstrip().encode('utf-8')
                toc += newsec
    if args.clipboard:
        sendToClipboard(toc)
    
if __name__ == '__main__':
    main()
Written on October 27, 2014