LoginSignup
9
12

More than 3 years have passed since last update.

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

Posted at

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

TL; DR

markdown-preview-modeの中に入ってるpreview.htmlhighlight.jsを当てましょう。これだけっ!

環境

emacs使ってて、markdown-preview-modeが使えることが前提です。この記事が参考になると思います。ありがとうございます。

コードハイライト

このままだと、リアルタイムプレビューはできるが、コードハイライトがされない、、、という状態になります。こんな感じです。Screen Shot 2020-05-02 at 8.15.08.png
これは辛い。なんとかしてコードハイライトさせたいですよね。ネットで調べると色々出てくるのですが、リアルタイムプレビューな環境でコードハイライト、というのがなかなかヒットしません。所詮HTMLなので、なんとかしてハイライトさせるとしたら、HTMLにhighlight.js当てるしかないな、、と思いました。

markdown-preview-modeの仕組み

markdown-preview-modeを見るとわかるのですが、markdownからHTMLを生成する際にはpreview.htmlをテンプレートにしているようです。ローカル環境だとどこに格納されているかは人それぞれですが、私は今はCaskを使っているので、私の環境では以下に格納されていました。
Screen Shot 2020-05-02 at 8.32.15.png

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

preview.htmlを編集しよう

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

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を当てて、WebSocketからデータ取得して書き込むたびにハイライトさせれば、リアルタイムにハイライトがされるはず、、!というわけで、早速ですが修正後はこちら。

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
いいですね!リアルタイムプレビュー環境下でも、ちゃんとコードがハイライトされるようになりました。

まとめ

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

9
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12