WordPressでソースコードをハイライトするならPrism.jsが多機能でおすすめ

ソースコードをハイライトするライブラリはいくつかありますが、全て試した中で一番使いやすかったPrism.jsについて紹介したいと思います。プラグインによって機能を拡張できたり、自分でも機能を追加することができます。

ダウンロード

ThemesはあとでCSSファイルを編集すればカスタマイズできるのでとりあえず気に入ったものを選びます。Languagesでハイライトしたいプログラミング言語を選びます。

Pluginsでは必要な機能だけ追加することができます。ここでは、よく使うと思われる行のハイライト行番号のプラグインを追加し、説明していきます。Remove initial line feed1行目の空行を削除してくれるので入れておきましょう。

カスタマイズが終わったら、JSファイルCSSファイルをそれぞれダウンロードします。

基本的な使い方

読み込み


<link href="prism.css" rel="stylesheet">
<script src="prism.js"></script>

ダウンロードしたファイルを読み込みます。

HTMLはエスケープ


add_filter('the_content', function($content) {
  $content = preg_replace_callback('/<pre(.*?)><code>(.+?)<\/code><\/pre>/s', function($matches) {
    $matches[2] = preg_replace(['/</', '/>/'], ['&lt;', '&gt;'], $matches[2]);
    return '<pre'.$matches[1].'><code>'.$matches[2].'</code></pre>';
  }, $content);
  
  return $content;
});

HTMLをハイライトするときは、<> はそれぞれ &lt; &gt; と記述する必要がありますが、毎回手作業で置き換えるのは大変なのでPHP側で置換します。


<!-- ダメな例 -->
</code></pre>

<!-- 正しい例 -->
&lt;/code&gt;&lt;/pre&gt;

ただし、この正規表現を使った方法だと、</code></pre> が含まれている場合、そこでタグが閉じられてしまって正しく置換されません。なので、そういう場合は手動で &lt; &gt; に置き換えます。

記法


<pre class="language-markup"><code>
  ここにソースコード
</code></pre>

class="language-" で始まるクラス名をつけます。ハイフンのあとはプログラミング言語名になりますが、Prism.jsで定義されているので以下のリンク先を確認してください。

スマホでのスクロール改善


pre[class*="language-"] {
  padding: 1em;
  margin: .5em 0;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: 0.3em;
}

スマホでスクロールを滑らかにできるように追加します。

行番号の表示

行番号を表示するにはLine Numbersプラグインが必要なので、チェックを入れてからダウンロードしてください。

記法


<pre class="language-markup line-numbers"><code>
  ここにソースコード
</code></pre>

line-numbers クラスを付けると行番号が表示されます。

開始行を指定


<pre class="language-markup line-numbers" data-start="3"><code>
  ここにソースコード
</code></pre>

data-start 属性で開始行を指定できます。

行番号プラグインの問題点

CSSを見ればわかりますが、行番号の横幅が width で設定されており、可変幅には対応していません。なので、行番号が4桁を超えると表示が崩れてしまいます。

行番号プラグインを修正

<code> に 行番号の幅分、padding を設定するようにします。


pre[class*="language-"] {
  padding: 1em;
  margin: .5em 0;
  font-size: 1em;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: 0.3em;
}
pre[class*="language-"] > code {
  display: inline-block;
  padding: 1em;
}
pre.line-numbers {
  position: relative;
  padding-left: 3.8em;
  counter-reset: linenumber;
}
.line-numbers .line-numbers-rows {
  position: absolute;
  pointer-events: none;
  top: 0;
  top: 1em; /* pre[class*="language-"] > code の padding-top */
  font-size: 100%;
  left: -3.8em;
  left: 1em; /* pre[class*="language-"] > code の padding-bottom */
  width: 3em;
  letter-spacing: -1px;
  border-right: 1px solid #999;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

Prism.hooks.add('complete', function(target) {
  var $target = $(target.element),
      $lineRows = $('.line-numbers-rows'),
      padding = parseInt($target.css('padding-left')) + $lineRows.outerWidth() + parseInt($lineRows.children().eq(0).css('padding-right'));
  
  $target.css('padding-left', padding);
});

Prism.jsにはWordPressのようにフックがあるので任意のタイミングで処理することができます。

行番号が増えても横幅が自動調整されるようになりました。

行ハイライト

行ハイライトするにはLine Highlightプラグインが必要なので、チェックを入れてからダウンロードしてください。

デフォルトだと横スクロールできるとき、背景色が途切れてしまうので少しカスタマイズします。


pre[class*="language-"] {
  padding: 1em;
  margin: .5em 0;
  font-size: 1em;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: 0.3em;
}
pre[data-line] {
  position: relative;
  padding: 1em 0 1em 3em;
}
.line-highlight {
  position: absolute;
  left: 0;
  left: -3em;
  right: 0;
  padding: inherit 0;
  margin-top: 1em;
  background: hsla(24, 20%, 50%,.08);
  background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
  background-color: #633b6a;
  pointer-events: none;
  line-height: inherit;
  white-space: pre;
  z-index: -1;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
pre[class*="language-"] > code {
  display: inline-block;
  position: relative;
  min-width: 100%;
  padding: 1em;
  z-index: 1;
}

ソースコードブロックの余白は <code> に設定します。


Prism.hooks.add('after-highlight', function(env) {
  var lines = env.highlightedCode.split('\n');
  
  for (var i = 0; i < lines.length - 1; ++i) {
    if (lines[i] === '') {
      lines[i] = '\n';
      continue;
    }
    lines[i] = '<div>' + lines[i] + '</div>';
  }
  
  $(env.element).html(lines.join(''));
});

iOSで、横スクロール可能なとき、ハイライト部分の背景色が右端まで伸びないバグがあったので、1行ずつ div 要素で囲みます。

使い方


<pre class="language-css" data-line="2-4,8"><code>
  ここにソースコード
</code></pre>

data-line に行数を設定することでハイライトさせることができます。カンマ区切りで複数指定でき、2-4 のようにハイフンでつなぐことで2〜4行と範囲指定もできます。

行番号と行ハイライトを同時に


pre[class*="language-"] {
  padding: 1em;
  margin: .5em 0;
  font-size: 1em;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: 0.3em;
}
pre[data-line] {
  position: relative;
  padding: 1em 0 1em 3em;
}
pre[class*="language-"] > code {
  display: inline-block;
  position: relative;
  min-width: 100%;
  padding: 1em;
  z-index: 1;
}
.line-highlight {
  position: absolute;
  left: 0;
  left: -3em;
  right: 0;
  padding: inherit 0;
  margin-top: 1em;
  background: hsla(24, 20%, 50%,.08);
  background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
  background-color: #633b6a;
  pointer-events: none;
  line-height: inherit;
  white-space: pre;
  z-index: -1;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
pre.line-numbers {
  position: relative;
  padding-left: 3.8em;
  counter-reset: linenumber;
}
.line-numbers .line-numbers-rows {
  position: absolute;
  pointer-events: none;
  top: 0;
  top: 1em; /* pre[class*="language-"] > code の padding-top */
  font-size: 100%;
  left: -3.8em;
  left: 1em; /* pre[class*="language-"] > code の padding-bottom */
  width: 3em;
  letter-spacing: -1px;
  border-right: 1px solid #999;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

Prism.hooks.add('after-highlight', function(env) {
  var lines = env.highlightedCode.split('\n');
  
  for (var i = 0; i < lines.length - 1; ++i) {
    if (lines[i] === '') {
      lines[i] = '\n';
      continue;
    }
    lines[i] = '<div>' + lines[i] + '</div>';
  }
  
  $(env.element).html(lines.join(''));
});

Prism.hooks.add('complete', function(target) {
  var $target = $(target.element),
      $lineRows = $('.line-numbers-rows'),
      padding = parseInt($target.css('padding-left')) + $lineRows.outerWidth() + parseInt($lineRows.children().eq(0).css('padding-right'));
  
  $target.css('padding-left', padding);
  
  $target.parent().children('.line-highlight').each(function() {
    $(this).appendTo($target);
  });
});

行番号と行ハイライトプラグインを同時に使用すると、ハイライトする .line-highlight 要素が <code> ではなく、<pre> 直下に移動してしまうので、<code> 直下になるように修正します。

横幅を飛び出してしまうときの対処法

例えば、max-width: 500px に指定していて、ソースコードの要素が overflow: auto なのに飛び出してしまうことがあります。どのような場合にこの現象が起きるのかは分かりませんが、対処法を紹介します。


<div class="prismjs">
  <pre class="language-css"><code>
    ここにソースコード
  </code></pre>
</div>

.prismjs {
  display: table;
  table-layout: fixed;
  width: 100%;
}

ソースコードブロックの外側を要素で囲み、CSSを記述します。