org-mode で記事が書けるようになりました

org-mode で記事を書きたい

今までの記事は全て SXML で直書きしていました。 しかし、ここ最近、 org-mode を本格的に使用するようになって、 いくらS式が好きだといってもさすがに SXML でブログを書くのは苦行であると気づきました。 今後 Guix の紹介記事のような重要な記事を書く予定があるので、 org-mode の強力なタスク管理を利用して記事を作成したいと考えています。

このブログは Haunt という、GNU Guile 製の静的サイトジェネレータを使用して生成しているのですが、 サポートしている文書の形式は, SXML, Texinfo, Skribe, CommonMark の4つのみであったため、 デフォルトでは org-mode は使用できませんでした。

既に org-mode に依存してしまっているので、 org-mode で記事が書けないのは致命的です。 この問題を、 Haunt を拡張することによって解決します。

Haunt の紹介

Haunt は GNU Guile (Scheme の処理系の一つ)で実装された静的サイトジェネレータで、 サイトの生成の仕方などを Guile で割と自由に拡張できるのが特徴的です。 このサイトでもブログ記事から対応する GitLab 上のソースコードにアクセスしたり、 記事の変更履歴を閲覧する機能が追加されています。 ソースコードは公開されているので、興味のある方は確認してみてください。

Haunt の基本的な使い方については 公式ドキュメント を参照していただくことにして、 今回の説明に必要なところだけ解説します。 haunt.scm というファイルでサイトをどのように生成するのかという設定を書き、 posts ディレクトリに記事を配置して haunt build というコマンドを実行すると良い感じにブログが生成されます。

haunt.scm で記事の reader を指定することができるので、 今回は org-reader という org-mode 用の reader を追加することで、 org-mode の記事を書けるように拡張します。

org-reader の実装について

commonmark-reader の実装を確認したところ、 記事のファイルからメタデータと SXML 形式のコンテンツに変換できれば良いことが分かりました。

Guile だけで org-mode の文書を SXML に変換するのは骨が折れそうなので、 Pandoc を使用して HTML に変換して、それを SXML に変換することで実現することにしました。

Pandoc を使用して HTML に変換する部分は org-string->html-string という手続きで実現しているのですが、 Guile でパイプの入出力を行なうプログラムを書くのが意外と難しかったです。 (ice-9 popen) が公式に提供している open-pipe* だと入力用のポートのみをクローズすることができないために、 エクスポートされていない手続きである open-process を使用しています。

(define (org-port->html-string port)
  (receive (read-port write-port pid)
      (open-process OPEN_BOTH "pandoc" "-f" "org")
    (put-string write-port (get-string-all port))
    (close-port write-port)
    (let ((result (get-string-all read-port)))
      (close-port read-port)
      (let ((status (cdr (waitpid pid))))
        (unless (zero? status)
          (error "pandoc return a non-zero status-code:" status)))
      result)))

非公開の手続きを使用しているので、正直微妙な気持ちになっていますが、 shell の代替として Guile が使えそうな雰囲気ですね。

org-reader 本体は下記のように実装しています。

(define org-reader
  (make-reader (make-file-extension-matcher "org")
               (lambda (file)
                 (call-with-input-file file
                   (lambda (port)
                     (let* ((metadata (read-metadata-headers port))
                            (html-string (org-string->html-string
                                          (get-string-all port))))
                       (values metadata
                               ((sxpath '(dummy *))
                                (xml->sxml
                                 (string-append "<dummy>"
                                                html-string
                                                "</dummy>"))))))))))

まとめ

org-mode でブログを書けるようになりました。

Haunt は Hackable な静的サイトジェネレータで、Guile を使って簡単にカスタマイズできて結構楽しいです。 自分でブログを作成する予定のある人は一度検討してみてください。