Railsアプリケーションにおいて、Markdownを表示する部分をSyntax Highlightに対応させる方法として、Highlight.jsを用いる方法を説明します。

前提

まず、Syntax Highlightを導入する前提として、以下を満たすものとします:

  1. メインアプリケーションとは疎結合に実装できること
  2. メンテナンスされているOSSであること
  3. テーマが豊富であること

以上を満たすものとして、今回 Highlight.js を選びました。

選定理由

一般的な方法として、Markdownパーサのレンダラに CodeRay を渡す方法があります。 ただ、サーバサイドで(短いとはいえ)CodeRay用の実装が必要になり、またテーマを自分で別途見つける必要があります。

Highlight.jsはMarkdownパーサが生成するHTMLをそのままSyntax Highlightに対応させ、公式のテーマも豊富です。 メンテナンスもしっかりされており(2016年3月現在)、Turbolinks上でも問題なく動作するため、Highlight.jsを選びました。

動作環境

  • Ruby on Rails: 4.2.5.1
  • Highlight.js: 9.1.0

方法

1. Redcarpetをインストールする

まず、Markdownパーサを導入する必要があります。 ここでは Redcarpet を用いる方法について説明します。

Gemfile に以下を追加し、 bundle install します。

gem 'redcarpet'

2. Highlight.jsをダウンロードする

次に、 vendor/assets/{javascripts,stylesheets} 下にHighlight.jsをダウンロードします。 テーマは デモ でプレビューできるので、好みのテーマをダウンロードします。

3. マニフェストファイルに追加する

上記assetsをマニフェストファイルに追加します(テーマは、例として hybrid を使用するものとします)。

app/assets/javascripts/application.js:

//= require highlight.min

app/assets/stylesheets/application.css:

/*
 *= require hybrid
 */

4. Highlight.jsを実行する

ページを表示する際にHighlight.jsを実行します。

app/assets/javascripts/application.js:

// Turbolinksを使用しない場合
$(document).on('ready', function() {
  hljs.initHighlightingOnLoad();
});

// Turbolinksを使用する場合
$(document).on('ready page:load', function() {
  hljs.initHighlighting.called = false;
  hljs.initHighlighting();
});

5. Helperを定義する

最後に、MarkdownをHTMLに変換するためのヘルパーを定義します。

app/helpers/application_helper.rb:

def markdown(text)
  extensions = {
    fenced_code_blocks: true
  }

  markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, extensions)
  markdown.render(text).html_safe
end

以上で導入は完了です。 あとは、任意のビューで以下のように出力すると、Syntax Highlightされた文字列が表示されます。

例:

<%= markdown(@post.content) %>

テーマのカスタマイズ

Highlight.jsによりハイライトされたHTMLには .hljs というクラスが付与されます。 これをスタイルシートで上書きすればよいです。

例:

.hljs {
  font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
}

問題点

上記は、当然ですがHTMLをJavaScriptでパースしています。 一般的なアプリケーションでは問題にならないとは思いますが、パフォーマンスをかなり気にする性質のものである場合、サーバサイドで実装しキャッシュするなどの方法の方がよいと思います。