はなゐろぐ

主に技術関係の覚え書きです。

HEXOとhighlight.jsでシンタックスハイライトし、ついでにファイル名も表示する

2019年10月29日

Qiitaのように、コードブロックにファイル名も表示します。HEXOが前提ですが、他のMarkdown記法のものなら応用がきくんじゃないでしょうか。

highlight.js を読み込む

任意の場所でスクリプト本体とテーマCSSを読み込みます。

<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/styles/rainbow.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/highlight.min.js"></script>

テーマCSSのデモはhighlight.js demoにあります。

また、highlight.min.jsはデモの「common」カテゴリの言語のみ対応のようなので、必要に応じてcdnjs.comから追加するとよいでしょう。例えばSCSSは含まれていないので、追加で読み込む必要があります。

<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/languages/scss.min.js"></script>

モジュールとして読み込むこともできます。ドキュメントを見るに、言語を選んでコンパイルもできるようなので、環境によってはこっちのほうが良さそう。

コードブロックを書く

普通にコードブロックを記述します。ファイル名を表示したい場合は言語名のあとにコロンでつなげます。逆に何も指定しなくてもhighlight.jsが自動で言語を認識してくれます。

```php:functions.php
ここにコード
(後略)
```

すると、こんな感じで出力されます。

<pre><code class="php:functions.php">ここにコード(後略)</code></pre>

highlight.jsを使うには、言語名だけのクラス(ここではphp)にする必要があるので、JavaScriptでなんとかします。クラスがなくても自動判定してくれるのですが、一応。

コードブロックのクラスを変更する

とりあえずこんな感じです。DOMContentLoadedのタイミングでページ内のクラス付きcode要素に対し、コロンがついていればそこで文字列を分割してクラスとdata属性にセットします。あとはhighlight.jsがよしなにしてくれます。

document.addEventListener('DOMContentLoaded', () => {
  // code要素にファイル名などを付与する
  var codes = document.querySelectorAll('code');
  if (codes) {
    Array.prototype.slice.call(codes).forEach(function (item) {
      // クラスを取得
      var classes = item.classList;
      if (classes.length > 0) {
        // ファイル名があればdata属性にセット
        if (classes[0].indexOf(':')) {
          var values = classes[0].split(':');
          var filename = values[1];
          if (filename) item.setAttribute('data-filename', filename);
          // ファイル名を削除して言語クラスに変更する
          item.classList.remove(classes[0]);
          item.classList.add(values[0]);
        }
      }
    });
  }
});

// highlight.jsによるシンタックスハイライト
hljs.initHighlightingOnLoad();

コードブロックにファイル名を表示させる

コードブロックにdata-filename属性を付与したので、それをもとにCSSを書きます。

.hljs {
  position: relative;
  border-radius: 2px;
  overflow: hidden;

  &[data-filename] {
    // ファイル名を表示する時だけ上に余白をあける
    padding-top: 1.8em;

    // 疑似要素にファイル名を表示
    &::before {
      content: attr(data-filename);
      position: absolute;
      top: 0;
      right: 0;
      padding: 0 0.3em;
      color: $font-color-default;
      font-size: 0.93em;
      background-color: rgba(#fff, 0.8);
      border-bottom-left-radius: 2px;
    }
  }
}

以上です!