VSCodeでMarkdownの自動フォーマット＆整形ルールを自由に設定

# TL;DR
(2023.07追記)

* [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)を入れているとMarkdownも自動フォーマットされる
    * Prettier v2までは日本語Markdown文書の整形ルールに問題があった
    * JS整形に比べて、Markdown整形はルール設定はほとんどできない
    * Prettier v3にアップグレードすると[いい感じになる](https://prettier.io/blog/2023/07/05/3.0.0.html)
* 整形ルールを自由に設定したければ
    * `.prettierignore`を作って`*.md`はフォーマットさせないように
    * [Remark](https://marketplace.visualstudio.com/items?itemName=mrmlnc.vscode-remark)をインストールすれば柔軟な整形ルール設定が可能

# この記事を書いた背景
(2023.07追記)

Prettier v2時代にハマったのがきっかけですが、v3.0になって見事に改善されました。めでたい！！！
[Prettier 3.0: Hello, ECMAScript Modules!](https://prettier.io/blog/2023/07/05/3.0.0.html)

原文引用

> **Stop inserting spaces between Chinese or Japanese and Western characters**
Previously, Prettier would insert spaces between Chinese or Japanese and Western characters (letters and digits). While some people prefer this style, it isn’t standard, and is in fact contrary to official guidelines. Please see [here](https://github.com/tats-u/prettier-plugin-md-nocjsp#why-this-plugin-is-needed) for more details. We decided it’s not Prettier’s job to enforce a particular style in this case, so spaces aren’t inserted anymore, while existing ones are preserved. If you need a tool for enforcing spacing style, consider textlint-ja or lint-md (rules space-round-alphabet and space-round-number).

DeepL訳

> **中国語または日本語と欧米語の文字の間にスペースを挿入しないようにした。**
以前は、Prettierは中国語または日本語と西洋文字（文字と数字）の間にスペースを挿入していました。このスタイルを好む人もいますが、標準ではありませんし、公式ガイドラインにも反しています。詳しくは[こちら](https://github.com/tats-u/prettier-plugin-md-nocjsp#why-this-plugin-is-needed)をご覧ください。この場合、特定のスタイルを強制するのはPrettierの仕事ではないと判断し、スペースは挿入されなくなり、既存のスペースは保持されるようになりました。スペーシングスタイルを強制するツールが必要なら、textlint-jaかlint-md（ルールspace-round-alphabetとspace-round-number）を検討してください。

「詳しくはこちら」で紹介されてるように、こんなプラグインが2022年にリリースされていたんですね。知らなかった！Qiita記事も見つけました。
[和欧文字間（漢字仮名と英数字の間）に半角スペースが挿入されないようにするPrettier Markdownプラグインを作った
](https://qiita.com/tats-u/items/bcbfe2bb4e71bf0a2b87)

Prettier v3ではこのプラグインの機能がデフォルトで入ったようです。
@tats-u さん、良い仕事してくださってありがとうございました！！！

ほとんどの方は、Prettier v3に移行すればこの記事の解説は不要だと思います。
しかし、やはり整形ルールは細かいところまで自分で調整したいのだ！という方は、[整形ルールを自由に設定する方法](#整形ルールを自由に設定する方法)から読んでください :smiley: 

当時の苦悩の記録として以下は残しておきますが、あくまでPrettire v2以前の問題として理解をお願いします。

## Markdownの自動フォーマットが意味不明だった
Visual Studio Codeで`editor.formatOnSave`を有効にすると、なぜかMarkdownのファイルも保存時に自動整形されちゃってました。

特にフォーマッタの設定をした覚えがないのに、やって欲しくないフォーマットを勝手にするので結構悩んでたのですよ。
例えば以下のように、英語・日本語が連続する文字を入れると

    PDFテンプレート
       ↓
    PDF テンプレート
ご丁寧に、境界にスペースを入れてくださるのです。
これはレンダリング後の表示にも影響してくるので勘弁してほしい。
英語圏の人は、こんな問題には気づかないんだろうなー。

## 犯人はPrettier
JavaScriptコードを自動フォーマットする目的で入れている[Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)が、Markdownも自動フォーマットしてくれてたのが原因でした。JS専用じゃなくて、言語の守備範囲はかなり広いのですな・・・

[Prettierのドキュメント](https://prettier.io/docs/en/options.html)を読んでも、JSのルールは調整できるんですが、Markdownのルール調整が全然できなくて、上記の「英語と日本語の境界にスペースが入る問題」は解決できませんでした。

`parser`を何とかしてやれば行けそうな気もするけど、ハードル高いのでやめておく。

# 整形ルールを自由に設定する方法

PrettierではMarkdownの整形ルール設定がほとんどできないので、自由に設定したければ他のツールでフォーマットする必要があります。

## PrettierのMarkdownフォーマットを無効化
まずはVSCodeの設定に以下を追記して、Markdownをフォーマットしないように設定します。

```
    "prettier.disableLanguages": [
        "vue",
        "markdown",
    ],
```

ちなみに`vue`はデフォルトなのでそのまま含んでますが、`vue`にはPrettier使いたければ含まなくてもOK。

## RemarkでMarkdownを独自にフォーマット
Markdownの自動フォーマットができて、自分で設定調整もできるVSCodeの機能拡張を探したところ、[Remark](https://marketplace.visualstudio.com/items?itemName=mrmlnc.vscode-remark)が一番良さげでした。

インストールして、コマンドパレットから`Remark: Beautify markdown code`を選ぶとmdファイルのフォーマットができます。なぜか`editor.formatOnSave`では動いてくれないので、「フォーマットする」というトリガは自分で操作しないとダメっぽいですね。

この時点で、最初の「英語と日本語の境界にスペースが入る問題」は解決されとります！あとは、もう少し自分好みの整形ルールに調整してあげるだけ。

整形ルールの調整は、VSCodeの設定ファイル内にこんな感じで書けます。

```
    "remark.format": {
        "rules": {
            "bullet": "*",
            "listItemIndent": 1,
            "fences": true,
        }
    },
```

`rules`の設定項目は、こちら本家Remarkドキュメントの`options`のプロパティが使えます。
https://github.com/remarkjs/remark/blob/master/packages/remark-stringify/readme.md#options

VSCodeのプラグインとしてもデフォルト値は持っていて、`package.json`にベタ書きされてました。
https://github.com/mrmlnc/vscode-remark/blob/master/package.json

ほとんどは本家Remarkの`default`そのまんまだけど、一部書かれてないプロパティもあったので、本家とは若干違うのかも。詳しくは見てません。

あと「英語と日本語の境界にスペースを入れる」設定を、そもそもどうやるのか分かりませんでしたw

# remark.js系ツールについて

## remarkとremark-lintの違い
僕が以前書いたこの記事
[Atomのlinter-markdownを.remarkrcで設定する方法](https://qiita.com/the_red/items/3e6294244cb81e91c121)

当時はVSCodeではなくAtom使ってたわけですが、そこでは[remark-lint](https://github.com/remarkjs/remark-lint)というのを使ってました。

今回のVSCode機能拡張で使われてる[remark](https://github.com/remarkjs/remark)とは別物で、でも両方同じGitHub Organizationの[remarkjs](https://github.com/remarkjs)配下のリポジトリです。

VSCode機能拡張の方に、こんなIssueも出てました。
[Implement remark-lint #4](https://github.com/mrmlnc/vscode-remark/issues/4)
なので、`remark-lint`の方が新しくて機能充実してるってことなのかな？

上の記事では`remark-lint`の設定を`.remarkrc`ファイルで書いてたんですけど、`remark`ではそのファイルが使えないっぽいです。できればVSCodeの設定じゃなくて、プロジェクト単位で`.remarkrc`を置いておきたいなーと思うので、対応してほしいですねー。

## PrettierのParserもremark-parseだった
[PrettierドキュメントのParserセクション](https://prettier.io/docs/en/options.html#parser)からリンク貼られてるのも、「remark-parse」という「remark」リポジトリ内のページでした。
https://github.com/remarkjs/remark/tree/master/packages/remark-parse

とにかくMarkdownを解析するならremarkって感じなんでしょうね。（テキトーな解釈）

## MDAST
そもそもremark.js系の色々なリポジトリからリンク貼られてるのが、こちらの「MDAST」というリポジトリ。
https://github.com/syntax-tree/mdast

remarkは、全体的にこのMDASTを使っているっぽいです。
MDASTやASTについては、この辺の記事が参考になりそう。（あまりしっかり読んでないけど、奥が深そうです・・・）
[markdownを直接ReactElementに変換するコンパイラを書いた](https://qiita.com/mizchi/items/ff498e1032ce79b8dd5c)
[QiitaとMarkdownとコンテンツオーサリング#Markdown AST](https://qiita.com/mizchi/items/17bc6c123207be7e6636#markdown-ast)

# おわりに
例のごとく、後半は色々と枝葉の情報ばかり調べちゃいました。

コードフォーマッタを作るときは、結局はその言語の構文解析が必要で、末端のツールは違っても最終的に同じツールに収束していくのが面白かったです。

あと「remark.js」って、全然別物として2種類あるんですね。。

今回使った「Markdownプロセッサ」がこちら。
https://remark.js.org/
もひとつが、「Markdownのスライドショーツール」
https://remarkjs.com/

紛らわしい。。。