コードのシンタックスハイライトを行うgemとして、Rougeが有名だと思います。
RougeにはDiffのシンタックスはありますが、Diffと各言語のシンタックスを同時に適用することができないので、それを実現するためのTipsを紹介します。
実は実現する方法はRouge内のIssueで議論されていました。これを使って作ってみます。
まずは1つのLexerに対してDiffの指定も行えるようにしてみます。
rubyの場合(Rouge::Lexers::Rubyはロードしている前提)
module Rouge
module Lexers
class DiffRuby < Ruby
prepend :root do
rule(/^\+.*$\n?/, Generic::Inserted)
rule(/^-+.*$\n?/, Generic::Deleted)
rule(/^!.*$\n?/, Generic::Strong)
rule(/^@.*$\n?/, Generic::Subheading)
rule(/^([Ii]ndex|diff).*$\n?/, Generic::Heading)
rule(/^=.*$\n?/, Generic::Heading)
end
end
end
end
これだけでRubyのシンタックスでDiffの指定も行えるようになります。
しかし、このままではRouge全ての言語に対してカスタムLexerを作成する必要があります。愚直にかくとかなり大変そうです。
そこで、以下のようなモンキーパッチを作成してみました。
(::Rouge::Lexer.all - [::Rouge::Lexers::Diff]).each do |lexer|
next unless lexer.ancestors.include?(::Rouge::RegexLexer) # RegexLexerを継承していないと構文の追加が行えないのでスキップ
klass_name = "Diff#{lexer.name.demodulize}"
::Rouge::Lexers.const_set(klass_name, Class.new(lexer) do |_klass| # クラスを生成する
title "Diff of #{lexer.title}"
tag klass_name.underscore
prepend :root do
rule(/^\+.*$\n?/, Generic::Inserted)
rule(/^-+.*$\n?/, Generic::Deleted)
rule(/^!.*$\n?/, Generic::Strong)
rule(/^@.*$\n?/, Generic::Subheading)
rule(/^([Ii]ndex|diff).*$\n?/, Generic::Heading)
rule(/^=.*$\n?/, Generic::Heading)
end
end)
end
これをRailsであればinitializerなどで実行すれば、Diffを適用したカスタムLexerを全ての言語に対して生成することができます。
Diff以外でも、全ての言語のシンタックスを拡張した別のシンタックスを追加したい時などで、同じ方法が使えると思います。
誰かの参考になれば幸いです。