日々、Emacsで作業しているみなさまは当然、markdownもEmacsでガリガリ書いておられることと思います笑。いろんな人がいろんなやり方でmarkdown執筆環境を作っていると思いますが、コードのハイライトをリアルタイムプレビュー環境でも実現したいなぁ、というのが今回のモチベーションです。
TL; DR
markdown-preview-modeの中に入ってるpreview.html
にhighlight.jsを当てましょう。これだけっ!
環境
emacs使ってて、markdown-preview-mode
が使えることが前提です。この記事が参考になると思います。ありがとうございます。
コードハイライト
このままだと、リアルタイムプレビューはできるが、コードハイライトがされない、、、という状態になります。こんな感じです。
これは辛い。なんとかしてコードハイライトさせたいですよね。ネットで調べると色々出てくるのですが、リアルタイムプレビューな環境でコードハイライト、というのがなかなかヒットしません。所詮HTMLなので、なんとかしてハイライトさせるとしたら、HTMLにhighlight.js当てるしかないな、、と思いました。
markdown-preview-mode
の仕組み
markdown-preview-modeを見るとわかるのですが、markdownからHTMLを生成する際にはpreview.html
をテンプレートにしているようです。ローカル環境だとどこに格納されているかは人それぞれですが、私は今はCaskを使っているので、私の環境では以下に格納されていました。
なので、このpreview.html
をいじれば、コードハイライトもされるだろう、、という発想です。
preview.html
を編集しよう
HTMLを見てみるとWebSocket()
でデータをとって、<p>Markdown preview</p>
をリアルタイム更新していることがわかります。
<!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からデータ取得して書き込むたびにハイライトさせれば、リアルタイムにハイライトがされるはず、、!というわけで、早速ですが修正後はこちら。
<!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>
たったこれだけです。これで改めてコードハイライトが効いているか確認してみます。
いいですね!リアルタイムプレビュー環境下でも、ちゃんとコードがハイライトされるようになりました。
まとめ
今回はmarkdown-preview-mode
環境下でコードハイライトを実現するための方法の1つをご提示しました。みなさま、他にもっと良いやり方があるよ!とか、知見があれば、是非おしえてください!!