Markdown Preview Enhanced の目次生成機能についての妙な発見

Markdown Preview Enhanced の目次生成機能

コマンドで実行

VSCodeMarkdown Preview Enhanced(以下MPE)で記事を書いています。 MPEには目次を自動生成する機能があります。

記事中の目次を生成したい位置(たいていは文頭?)にカーソルを置いて、コマンドパレット(F1 または Ctrl + Shift + P)を開き、「Markdown Preview Enhanced: Create TOC」を実行。すると次のようなコードが挿入されます。

<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->

あとは記事中の見出しを自動的に見て勝手に目次を作ってくれます。

これはMPEのCode Chunkという機能(Markdownファイルの中で簡単なコードを実行して結果を挿入する)を使ってるっぽいです。パラメータで目次の外見をどう形成するかの設定を渡してます。

depthFrom : 目次を開始する見出しのレベル。デフォルトはdepthFrom=1H1から始まる。

depthTo : どこまで目次を生成するかの見出しレベル。デフォルトはdepthTo=6H6まで。

orderedList : orderedList=trueでオーダードリストによる章番号付きリスト。デフォルトはfalseで普通の番号なしリスト。

[TOC]で挿入

実際はコマンドを使わなくても、挿入したい場所に[TOC]と記入すれば挿入されます。この方が簡単。

また、プレヴュー画面の右クリックでHTML書き出しすると、コマンドや[TOC]記法を挿入しなくてもHTMLに目次表示用のハンバーガーメニューボタンが付いて、クリックすると目次が表示されます。

Front-Matterで制御する

コマンドに渡したパラメータは、front-matterで制御することも可能です。

公式ドキュメント

front-matterで目次を制御する際の書き方サンプル

---
title: "Markdown Sample"
date: 2020-12-14T22:26:52+09:00
toc:
  depth_from: 1
  depth_to: 4
  ordered: false
---

キーtoc以下に上記のような感じで書きます。パラメータのキー名がコマンドの場合と多少違うので注意。

柔軟な生成機能

MPEのこの素晴らしい目次自動生成機能ですが、特に助かるわ−と思うのは、文中の見出しレベルが厳密に1段ずつの下降をしていなくても柔軟に対応して生成してくれる点です。言ってることわかりますか?

例えば次のような順で見出しを書いた場合。

## H2の見出し
#### H4の見出し

文章........

### H3の見出し

文章........

見出しレベルがH2からH3を経ずにいきなりH4に飛んで、またH3に戻っています。JavaScriptプラグインや、静的サイトジェネレータに内蔵されたMarkdownパーサでは、こういう場合にうまく目次が生成されないものがあります。MPEの目次生成機能ではこれも柔軟に対応してくれます。結果的には「H2の見出し」の下に、同レベルで「H4の見出し」と「H3の見出し」が並びます。

MPEで書いた記事を他のブログなどに貼り付けて使いまわす場合などは、ルールを守らないとうまくいかないわけですが、MPEの中で完結させるなら気にしなくていいわけです。もちろん人によってこの柔軟さは嫌だ、もっと整然と見出しを付けたい人も居るでしょうから、見出しの階層の崩れに気付き難いという点では完全なメリットとも言いにくいですが。

目次がうまく生成されない場合

たまに文章のある部分から下の目次が生成されないことあります。自分なりに小一時間ハマったのでメモっておきます。

Markdown記法でブロック要素を形成するものがあります。その中でも表とかコードブロックとかは注意が必要。表では試していませんが、表の書き方は明確なルールに従わないとテーブルそのものが形成されにくいのでわかりやすいですが、問題はコードブロック。

Markdownのコードブロックの書き方を載せたい場合、コードブロックの中に、さらにコードブロック記法で書くことになります。内側のコードブロック記法はパースさせたくないわけです。この場合MPEには  (backtick)3つで囲む記法と、チルダ)3つで囲む記法の2種類あるのでそれを活用します。例えばこんなふうに。

 ~~~
 ```javascript
 var oldUnload = window.onbeforeunload;
 window.onbeforeunload = function() {
     saveCoverage();
     if (oldUnload) {
         return oldUnload.apply(this, arguments);
     }
 };
 ```
 ~~~

で、い〜っぱいコードを書いているうちに、直前のコード例を1行変更するだけなので、変更の無い箇所は省略して次のように書いていました。

 ~~~
 ```javascript {.line-numbers}
    ....................(略)....................
 ~~~

プレビューや書き出したHTML上では何の問題もなく、意図したとおりに表示されます。でもこれ内側のコードブロック記法の「受け」となる backtick x 3 が無いですよね。プレヴュー上問題が無くても、おそらく内部的にはMarkdownパーサが内側のコードブロック記法もパースしていて、閉じの記法が存在しないことでの何らかのエラーが起きているのではと想像しています。

で、このままだとこの「受け」となる backtick x 3 が無い部分から後ろの目次が全部生成されなくなってしまいました。この原因を発見すること小一時間(泣)

そこで該当箇所をちゃんと堅苦しくも次のように書くことで、見事に目次が最後までレンダリングされました。

 ~~~
 ```javascript {.line-numbers}
    ....................(略)....................
 ```      <= ちゃんとブロック終了の記法を書く
 ~~~

他のブロック要素でも似たような現象あるかもわかりません。一見目次とは関連が無く、分かりにくいので要注意です。