7rikazhexde’s tech log

技術的な興味関心、備忘録、アウトプットなどを書いています。

mdformat + mkdocs(Admonitions)で意図した表示にならない現象と解決方法について

背景

以前以下の記事でMkdocsについて紹介しました。

7rikazhexde-techlog.hatenablog.com

記事の中では言及していなかったのですが、私はMarkdown用のフォーマッターとしてmdformatを使用しています。

公式ドキュメント:

mdformat.readthedocs.io

公開しているGitHub Pagesもその後更新を続けており、いくつか機能追加(多言語対応,クッキー設定,GA4対応,レイアウト変更)をしました。

mkdocsのレイアウトや設定についてはTipsページで紹介しようと考えています。

7rikazhexde.github.io

記事を更新する中で、大抵は公式のドキュメントを参照して解決できていたのですが、mdformat + mkdocs(Admonitions)の組み合わせでは意図した表示にすることができず、解決方法もすぐには見つけることができませんでした。

結論から言えばmdformatのプラグインを使用することで解決できたのですが、使用方法に指定がありました。

本記事では発生した現象と調査内容、解決方法について紹介します。

発生した現象について

mkdocsではadmonitionsという文書内にメモ、ヒント、警告などが目立つようなスタイルで表示してくれる機能です。サンプルとして以下を作成し、poetry run mkdocs serveすると、以下のような表示になります。

OKケース

!!! info
    - test1
    - test2

example-mkdocs-admonitions-ok

しかし、これにpoetry run mdformat .を実行すると以下のように変換されてしまいます。

NGケース

!!! info
\- test1
\- test2

example-mkdocs-admonitions-ng

解決方法

まず、mdformatのissueを調べました。すると以下のissueが見つかりました。

github.com

これは無視する対象をHTMLのコメントで囲むという方法ですが、2023/08/12時点で解決まで至っていません。

処理的には難しくはないため実際に作成することにしました。
型指定もなく上記例のみの確認のため参考にしないほうが良いと思いますが、やりたいことはできました。

def replace_mdformat_ignore(input_file, output_file, target_text):
    with open(input_file, "r", encoding="utf-8") as f:
        lines = f.readlines()

    converted_lines = []
    ignore_mode = False

    for line in lines:
        if "<!-- mdformat ignore start -->" in line:
            ignore_mode = True
            converted_lines.append(line)
        elif "<!-- mdformat ignore end -->" in line:
            ignore_mode = False
            converted_lines.append(line)
        elif ignore_mode:
            if line.strip() != "" and target_text not in line:
                line = line.replace(r"\-", "-")
                converted_lines.append("    " + line)
            else:
                converted_lines.append(line)
        else:
            converted_lines.append(line)

    with open(output_file, "w", encoding="utf-8") as f:
        f.writelines(converted_lines)


if __name__ == "__main__":
    target_text = "!!! info"

    input_file = "./docs/index.md"
    output_file = "./docs/index.md"
    replace_mdformat_ignore(input_file, output_file, target_text)

    input_file = "./docs/index.en.md"
    output_file = "./docs/index.en.md"
    replace_mdformat_ignore(input_file, output_file, target_text)

個別にスクリプトを作成する方法も良いですが、同じような問題に取り組んでいるプロジェクトがないかと思い調べることにしました。

すると、mdformatにはPlugins機能があることがわかりました。 そして、MkDocsのadmonitionsのためのmdformatプラグインとして、mdformat-admonというプラグインが存在することがわかりました。

github.com

admon以外にもmkdocsの記法に合わせたプラグインが複数存在していました。

github.com

注意点

公式ドキュメントのContributingに記載されていますが、プラグインを使用するためにはpre-commitで実行する必要があります。
mdformat単体では実行できないため注意が必要です。(もしかするとやり方があるかもしれませんが、確認できませんでした。)

使用方法

pre-commitについては割愛します。詳細は公式ドキュメントを参照ください。
.pre-commit-config.yamlは以下のように記載します。

repos:
  - repo: https://github.com/executablebooks/mdformat
    rev: 0.7.16
    hooks:
      - id: mdformat
        additional_dependencies:
          - mdformat-admon # ここに追加する

結果、NGケースのような結果にはならず期待する表示を確認することができました。

補足: 個別にmdformatを使用する場合

事前の動作確認でpre-commitで全てのフックを実行する場合は、poetry run pre-commit run --all-filesで実行できますが、個別に実行したい場合はpre-commitのhooksで指定したidを指定します。

1. pre-commitフックで個別にmdformatする場合

git add your_file.md  # 対象のMarkdownファイルをステージング
poetry run pre-commit run mdformat

2. 未ステージング状態のファイルに対してもフックを実行する場合

poetry run pre-commit run mdformat --all-files

補足: その他フォーマッターとの差異について

開発環境でVSCodeを使用している場合はmarkdown用のフォーマッターとして以下のようなものがあります。

  • Prettier - Code Fomatter
  • Markdown All in One
  • markdownlint

この中でデフォルトで問題なく動作するのはMarkdown All in Onemarkdownlintです。

Markdownファイルを全て選択(Ctrl + a) > 右クリック > ドキュメントのフォーマット…(2つある内の下で「…」の方) > Prettier - Code Fomatter

Prettierでは以下のように変換されてしまいます。

!!! info - test1 - test2

また、mdformatではプラグインが必要でしたが、Markdown All in Onemarkdownlintはデフォルトで上記NGケースのような変換にはなりませんでした。

まとめ

mdformat + mkdocs(Admonitions)で意図した表示にならない現象と解決方法について紹介しました。

mdformatは公式ドキュメントの通り、フォーマットの変更には慎重のようですが、pre-commitによるプラグインが複数存在するため、mkdocsに限らず、目的に応じてプラグインを使用することは有用だと思います。

Welcome to the mdformat developer docs! We’re excited you’re here and want to contribute. ✨

Please discuss new features in an issue before submitting a PR to make sure that the feature is wanted and will be merged. Note that mdformat is an opinionated tool that attempts to keep formatting style changing configuration to its minimum. New configuration will only be added for a very good reason and use case.

以上です。