geeknoteで表が書けない

一昨日いれたgeeknote。markdown記法が使えるのはいいんですが、GFMが書けないので表が書けない。ちょっとソースをのぞいてみると、evernoteから読んだテキストを pyton-markdown2 でHTMLに変換している模様。 editor.py の該当部分がこれ。

    def textToENML(content, raise_ex=False, format='markdown'):
        """
        Create an ENML format of note.
        """
        if not isinstance(content, str):
            content = ""
        try:
            content = unicode(content, "utf-8")
            # add 2 space before new line in paragraph for creating br tags
            content = re.sub(r'([^\r\n])([\r\n])([^\r\n])', r'\1  \n\3', content)
            if format=='markdown':
              contentHTML = markdown.markdown(content)

              soup = BeautifulSoup(contentHTML, 'html.parser')
              Editor.checklistInSoupToENML(soup)

なんでできんのじゃ

python-markdownを調べてみると、現行バージョンのpython-markdown2ではGFMもちゃんと扱えて、表を作成する場合には markdown()にextras引数を配列で与えればいい模様。

contentHTML = markdown.markdown(content,extras=["tables"])

対話式のpythonで試してみる。

$ python
Python 2.7.6 (default, Jun 22 2015, 18:00:18) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import markdown2
>>> text="|title|title|\n|----|----|\n|col1|col2|\n|col3|col4|\n"
>>> print text
|title|title|
|----|----|
|col1|col2|
|col3|col4|

>>> markdown2.markdown(text)← extrasを与えない場合
u'<p>|title|title|\n|----|----|\n|col1|col2|\n|col3|col4|</p>\n' 
>>> markdown2.markdown(text,extras=["tables"]) ←extrasでtablesを指定した場合。ちゃんと表形式のHTMLに変換している
u'<table>\n<thead>\n<tr>\n  <th>title</th>\n  <th>title</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n  <td>col1</td>\n  <td>col2</td>\n</tr>\n<tr>\n  <td>col3</td>\n  <td>col4</td>\n</tr>\n</tbody>\n</table>\n'
>>> 

ソースをいじる

本当ならば、geeknote setting extras="tables"とかって指定してからできるようにすればいいんだろうけどね。面倒なのでeditor.pyを直接いじっちゃう。
markdown()を読んでいるところを以下のように変更。

contentHTML = markdown.markdown(content,extras=["tables"])

試してみる

さっそくやってみる。。うまくいかない。上記でextrasを指定しなかった時と同じ結果になります。なんでじゃ。

なんか余計なことやっとんな

ソースを眺めること2時間。対話式で検証したときと何がちがうんだと検証した結果、makrdown()を読んでいる4行前の正規表現に原因があることを発見。この部分です。

# add 2 space before new line in paragraph for creating br tags
content = re.sub(r'([^\r\n])([\r\n])([^\r\n])', r'\1  \n\3', content)

コメントで注釈している通り、改行の前に2つのスペースを挿入しています。
conetntがこうだったら、

# hoge
## fuga
* item
* item

この正規表現での変換でこうなります。□はスベース。

# hoge□□
## fuga□□
* item□□
* item

この変換のためにGFMで表形式と思って書いたところが、表形式と認識されないことが原因でした。

ではこうしましょう

いろんな回避策は思いつくんですが、単純に。表形式のGFMの場合、|\nで終わっていると仮定して、その場合にはスペースを入れない*1とする。正規表現での変換はこう。

content = re.sub(r'([^\r\n|])([\r\n])([^\r\n])', r'\1  \n\3', content)

パイプがあって、改行がある場合はマッチしないので、変換されない。

これでようやく表形式で保存できました。

ついでに

保存するときにBeatifulSoupのワーニングがでるのを修正。一昨日入れたhtml2text()の文法違いも合わせたdiffはこちら。本当はgithubへpushするべきなんだろうけどな。

79c79
<         soup = BeautifulSoup(contentENML.decode('utf-8'))
---
>         soup = BeautifulSoup(contentENML.decode('utf-8'),'html.parser')
101c101
<         content = html2text.html2text(str(soup).decode('utf-8'), '', 0)
---
>         content = html2text.html2text(str(soup).decode('utf-8'), '')
169c169
<             content = re.sub(r'([^\r\n])([\r\n])([^\r\n])', r'\1  \n\3', content)
---
>             content = re.sub(r'([^\r\n|])([\r\n])([^\r\n])', r'\1  \n\3', content)
171c171
<               contentHTML = markdown.markdown(content)
---
>               contentHTML = markdown.markdown(content,extras=["tables"])

*1:実際の文法ではパイプで終わってなくても可