给 Nanoc 添加 Org mode 支持

2017-05-27

1 编写 Org mode Filter

Nanoc 没有提供现成的 Org mode 支持,需要编写自己的 Filters 把 Org mode 转化成 HTML。虽然我不懂 Ruby,还是照猫画虎外加 Google 实现了一个能用的 Org mode 的 Filter

require 'tempfile'

class OrgFilter < Nanoc::Filter
  identifier :org

  def run(content, params = {})
    file = Tempfile.new(['nanoc', '.org'])
    file.write(content)
    file.close
    system("emacs --batch --load init.el #{file.path} --eval '(org-html-export-to-html nil nil nil t)'")
    html = "#{File.dirname(file.path)}/#{File.basename(file.path, '.org')}.html"
    file.unlink
    File.read(html)
  end
end

2 安装新版本的 Org mode

考虑到 Emacs 自带的 Org mode 版本比较旧,Emacs 25.2 (2016/09) 自带了 Org mode 8.2.10 (2014/10),我更希望用 Org mode 9。由于 package.el 的限制,无法更新系统自带的包(其实是可以的,只是有些复杂),直接安装 org-plus-contrib 这个包。

刚开始的时候我直接用的是 .emacs.d/init.el ,但是发现它太慢了,导出一个简单的 Org 文件就需要 24 多秒

$ time emacs --batch --load ~/.emacs.d/init.el ~/foo.org --eval '(org-html-export-to-html nil nil nil t)'

real    0m24.808s
user    0m3.403s
sys     0m1.017s

实在太慢了,于是就特地写了一个简单的 init.el

(require 'package)

;; Use Emacs China's ELPA Mirror since I live in China
(setq package-archives
      '(("org"    . "http://elpa.emacs-china.org/org/")
        ("melpa"  . "http://elpa.emacs-china.org/melpa/")))

(setq package-user-dir (locate-user-emacs-file "elpa-nanoc"))

(package-initialize)

(unless (and (package-installed-p 'org-plus-contrib)
             (package-installed-p 'htmlize))
  (package-refresh-contents)
  (package-install 'org-plus-contrib)
  (package-install 'htmlize))

(setq org-html-htmlize-output-type 'css)

这样一来就只需要 1 秒左右了

$ time emacs --batch --load init.el ~/foo.org --eval '(org-html-export-to-html nil nil nil t)'

real    0m0.923s
user    0m0.848s
sys     0m0.048s

3 语法高亮

如果有 htmlize.el 的话,Org mode 导出 HTML 时自带代码高亮,而用不着 Pygments 、highlight.js 之类的第三方工具,并且我没发现这类工具有支持 Emacs Lisp 的,至多能支持到 Common Lisp 或 Scheme。

但在 Emacs 的 Batch mode 下会失效,我不清楚具体的原因,但不教我太意外:默认 Org mode 试图把 CSS 信息 inline 在 HTML 中,但 Batch mode 下 Font Lock mode 不会生效,自然就没有任何高亮?解决方法是

(setq org-html-htmlize-output-type 'css)

这样只把代码中各个元素的类用 CSS 类标示出来,如 class="org-keyword"class="org-string" ,Emacs 在 Batch mode 下能知道这些信息。之后还需要在 CSS 文件中规定这个类的属性,可以 M-x org-html-htmlize-generate-css 根据你的 Emacs 目前的主题生成 CSS 文件,也可以从别人那里下载,如 fniessen/org-html-themes

4 使用 Org mode Filter

Rules 文件中添加一条 compile 规则

compile '/**/*.org' do
  filter :org
  layout '/default.*'
  # Org file must be named like 2017-05-use-nanoc-with-org-mode.org
  y,m,slug = /([0-9]+)\-([0-9]+)\-([^\/]+)/.match(item.identifier).captures
  write "/blog/#{y}/#{m}/#{slug}/index.html"
end

最后还要告诉 Nanoc .org 文件不是 binary 文件,方法是把 org 添加到 nanoc.yaml 中的 text_extensions 里面。

这种方法速度必然会慢上不少,但我目前还可以接受,以后即便 Org 文件数量增多,由于缓存的缘故,耗时也不一定就会显著增加。

$ nanoc
Loading site… done
Compiling site…
      create  [1.19s]  output/blog/2017/05/use-nanoc-with-org-mode/index.html
      create  [0.01s]  output/feed.xml
      create  [0.00s]  output/htmlize.css
      create  [0.00s]  output/index.html
      create  [0.00s]  output/main.css
      create  [0.00s]  output/main.js

Site compiled in 1.22s.

Tags: ,

加载 Disqus 评论