このHakyllサイトのシンタックスハイライターをhighlight.jsからPygmentsに移行しました
highlight.jsへの不満
このサイトではhighlight.jsをコードのシンタックスハイライトに利用していました.
これには以下の問題がありました.
- Emacs Lispに対応していない
- フロントエンドでの変換なのでクライアントがアクセスするたびに無駄なオーバーヘッドがかかる
正直パフォーマンスはどうでも良いのですが, Emacs Lispに対応していないのは致命的です.
Pygmentsへの移行
PygmentsはPythonで書かれたシンタックスハイライターで多くの言語をサポートしているのが特徴です.
これでサーバ側でシンタックスハイライトを行うようにします.
Pandoc自体のシンタックスハイライトを無効化する
Pandoc自体に jgm/skylighting: A Haskell syntax highlighting library with tokenizers derived from KDE syntax highlighting descriptions というKateのXMLを利用したシンタックスハイライト機能があります.
これもそこそこの言語に対応していて, 普通に実用的なのですが, Emacs Lispには対応していません.
今回は本物のPygmentsを利用するのでこの機能はwriterHighlightStyle
をNothingにすることで無効化します.
HTML内部のコードを変換する方法がわからない
てっきりPygments自体にHTMLのpre
内に入ったコードを変換するコマンドがあるものだと思っていましたが,
無いようですね.
コードブロックだけを渡す必要があるようです.
ブロックだけを置換する
bottomUpM
というPandoc
データ型をたどってブロックを受けたわすPandocの関数と,
pandocCompilerWithTransformM
というPandoc型を変換関数にかけるHakyllの関数を使えば変換関数にブロックを渡せます.
あとは拡張子は適当に類推しておわり.
pandocCompilerCustom :: Compiler (Item String)
pandocCompilerCustom
= let extensions =
disableExtension Ext_smart $
enableExtension Ext_ignore_line_breaks $
readerExtensions defaultHakyllReaderOptions
transform (CodeBlock (_identifier, classes, _keyValue) str)
= let fileExtension = takeExtension (unwords classes)
fileKind = if null fileExtension then unwords classes else tail fileExtension
in RawBlock (Format "html") <$>
unixFilter "pygmentize"
(["-f", "html"] <>
if null fileKind then [] else ["-l", fileKind])
str
transform x = return x
in pandocCompilerWithTransformM
defaultHakyllReaderOptions
{ readerExtensions = extensions
}
defaultHakyllWriterOptions
{ writerHTMLMathMethod = MathJax ""
, writerSectionDivs = True
, writerExtensions = extensions
, writerHighlightStyle = Nothing
}
(bottomUpM transform)
JSXへの対応
PygmentsはデフォルトではJSXに対応していないようです.
fcurella/jsx-lexer: a JSX lexer for pygmentsを入れて解決. 拡張可能なのは良いですね.
思わぬ副産物
Pygmentsは対応していない言語が渡された時はしっかりエラーを出してくれるのでservice
とか雑な言語名を与えていたものをしっかりエラーとして検出してくれました.
CSSを用意する
HTMLは出力できたので, PygmentsのSolarized Dark向けのCSSが必要です.
スタイルを探したら以下のリポジトリが引っかかりました.
gthank/solarized-dark-pygments: A Pygments style based on the dark background variant of Solarized
でもCSSを取得する方法が何処にも書いていません. 困りました.
いろいろ調べたら, 出力したいスタイル定義を読み込んだあと, 以下を実行すれば取得できるようでした.
from pygments.formatters import HtmlFormatter
print(HtmlFormatter(style=Solarized256Style).get_style_defs())
しかしこのスタイルを適用してみましたがあまり好みのものではありませんでした. というかこれSolarizedなんですかね…?
と思ったら微妙だったのは256版を出力していたからでした.
print(HtmlFormatter(style=SolarizedStyle).get_style_defs())
で良いものが出てきました.
solarized-dark-pygmentsはMITライセンスなのでSCSSのコメントにリンクとライセンス文章を貼り付けて使うことにします. 今回の場合コードの生成物が著作物になるのかはよくわかりませんが, まあ素直にリスペクトを示しておきましょう.
バグ報告求む
大体は正しくなることを確認しましたが, 全てを確認したわけではないです. 崩れているのを発見したら, 情報提供をお願いします.