Emacs + markdownで快適なリアルタイムプレビューを！(コードハイライト編)

日々、Emacsで作業しているみなさまは当然、markdownもEmacsでガリガリ書いておられることと思います笑。いろんな人がいろんなやり方でmarkdown執筆環境を作っていると思いますが、コードのハイライトをリアルタイムプレビュー環境でも実現したいなぁ、というのが今回のモチベーションです。

# TL; DR
[markdown-preview-mode](https://github.com/ancane/markdown-preview-mode)の中に入ってる`preview.html`に[highlight.js](https://highlightjs.org/)を当てましょう。これだけっ！

## 環境
emacs使ってて、`markdown-preview-mode`が使えることが前提です。[この記事](https://qiita.com/No_217/items/afd39b0828b444a160da)が参考になると思います。ありがとうございます。

## コードハイライト
このままだと、リアルタイムプレビューはできるが、コードハイライトがされない、、、という状態になります。こんな感じです。![Screen Shot 2020-05-02 at 8.15.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271670/4e3f65a9-deb0-26f2-3a95-c04611001f1f.png)
これは辛い。なんとかしてコードハイライトさせたいですよね。ネットで調べると色々出てくるのですが、リアルタイムプレビューな環境でコードハイライト、というのがなかなかヒットしません。所詮HTMLなので、なんとかしてハイライトさせるとしたら、HTMLに[highlight.js](https://highlightjs.org/)当てるしかないな、、と思いました。

## `markdown-preview-mode`の仕組み
[markdown-preview-mode](https://github.com/ancane/markdown-preview-mode)を見るとわかるのですが、markdownからHTMLを生成する際には`preview.html`をテンプレートにしているようです。ローカル環境だとどこに格納されているかは人それぞれですが、私は今は[Cask](https://cask.readthedocs.io/en/latest/)を使っているので、私の環境では以下に格納されていました。
![Screen Shot 2020-05-02 at 8.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271670/bc54226b-b2db-58f0-f287-51540fc18c5e.png)

なので、この`preview.html`をいじれば、コードハイライトもされるだろう、、という発想です。

## `preview.html`を編集しよう

HTMLを見てみると`WebSocket()`でデータをとって、`<p>Markdown preview</p>`をリアルタイム更新していることがわかります。

```html:preview.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui">
    <title>Markdown preview</title>
    ${MD_STYLE}
    <script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
    ${MD_JS}
    <script>
     (function($, undefined) {
         var socket = new WebSocket("ws://${WS_HOST}:${WS_PORT}");
         socket.onopen = function() {
             console.log("Connection established.");
	     socket.send("MDPM-Register-UUID: ${MD_UUID}");
         };
         socket.onclose = function(event) {
             if (event.wasClean) {
                 console.log('Connection closed gracefully.');
             } else {
                 console.log('Connection terminated.');
             }
             console.log('Code: ' + event.code + ' reason: ' + event.reason);
         };
         socket.onmessage = function(event) {
             $("#markdown-body").html($(event.data).find("#content").html()).trigger('mdContentChange');
             var scroll = $(document).height() * ($(event.data).find("#position-percentage").html() / 100);
             $("html, body").animate({ scrollTop: scroll }, 600);
         };
         socket.onerror = function(error) {
             console.log("Error: " + error.message);
         };
     })(jQuery);
    </script>
  </head>
  <body>
    <article id="markdown-body" class="markdown-body">
      <p>Markdown preview</p>
    </article>
  </body>
</html>

```

なので、このHTMLに[highlight.js](https://highlightjs.org/)を当てて、WebSocketからデータ取得して書き込むたびにハイライトさせれば、リアルタイムにハイライトがされるはず、、！というわけで、早速ですが修正後はこちら。

```html:preview.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui">
    <title>Markdown preview</title>
    ${MD_STYLE}
    <script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
    ${MD_JS}

    <!-- ↓highlight.jsとCSSを当てましょう。CSSはいろんな種類があります。
         私はtomorrow.cssというものに落ち着きました。詳しくはhightlightのドキュメント見てください -->
    <link rel="stylesheet"
	  href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.1/styles/tomorrow.min.css">
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.1/highlight.min.js"></script>
    <!-- ネットでよく見る<script>hljs.initHighlightingOnLoad();</script>は今回は不要です。
         WebSocketでリアルタイムにレンダリングするので、OnLoadでhighlightしても意味がないですよね -->
    
    <script>
     (function($, undefined) {
         var socket = new WebSocket("ws://${WS_HOST}:${WS_PORT}");
         socket.onopen = function() {
             console.log("Connection established.");
	     socket.send("MDPM-Register-UUID: ${MD_UUID}");
         };
         socket.onclose = function(event) {
             if (event.wasClean) {
                 console.log('Connection closed gracefully.');
             } else {
                 console.log('Connection terminated.');
             }
             console.log('Code: ' + event.code + ' reason: ' + event.reason);
         };
         socket.onmessage = function(event) {
             $("#markdown-body").html($(event.data).find("#content").html()).trigger('mdContentChange');
             var scroll = $(document).height() * ($(event.data).find("#position-percentage").html() / 100);
             $("html, body").animate({ scrollTop: scroll }, 600);

	     // ここですね。pre codeタグを全て拾って、highlight処理をかけます。
	     document.querySelectorAll('pre code').forEach((block) => {
		 hljs.highlightBlock(block);
	     });
	 };
         socket.onerror = function(error) {
             console.log("Error: " + error.message);
         };
     })(jQuery);
    </script>
  </head>
  <body>
    <article id="markdown-body" class="markdown-body">
      <p>Markdown preview</p>
    </article>
  </body>
</html>

```

たったこれだけです。これで改めてコードハイライトが効いているか確認してみます。![Screen Shot 2020-05-02 at 8.48.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271670/3b4f4589-3bd2-7883-547c-0867edbe3db2.png)
いいですね！リアルタイムプレビュー環境下でも、ちゃんとコードがハイライトされるようになりました。

## まとめ
今回は`markdown-preview-mode`環境下でコードハイライトを実現するための方法の１つをご提示しました。みなさま、他にもっと良いやり方があるよ！とか、知見があれば、是非おしえてください！！