VSCodeでAsciidoc+mermaidのプラグインを作成する

# はじめに
VSCodeでasciidocを編集する際に、[asciidoctor-vscode](https://github.com/asciidoctor/asciidoctor-vscode)を使用していましたが、
図表の表示を行うkrokiがレンダリングの度にネットワークへ接続するのが少々嫌らしいと感じていました。
実際に使用したい図表はmermaidぐらいなので、asciidoctor.jsとmermaid.jsを組み合わせた
VSCodeのプラグインを作成してみました。（何故かこの組み合わせのプラグインが見つかりませんでした。）
プラグインはオフラインでも動作することを前提にしています。

作成したプラグインはこちらになります。

https://github.com/bluexe203/asciidocplugin

動作イメージ
![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1783262/647e66ed-7e40-3011-0241-d1108c816e43.png)

# VSCodeのプラグインの作成方法
環境構築手順について白亜さんの記事を参考にさせていただきました。

https://qiita.com/hakua-doublemoon/items/cfb638fe1ab43ca42cb8

## asciidoctorとmermaidを組み合わせる
ライブラリは以下のバージョンを使用しています。
[asciidoctor.js : 2.2.5](https://github.com/asciidoctor/asciidoctor.js/)
[mermaid : 8.13.3](https://github.com/mermaid-js/mermaid)

理想的にはVSCode上のtypescriptのコードでasciidoc→html、mermaid→svgが出来ればよいのですが、
mermaidがVSCodeからinitializeする際に必ずエラーになるため、

1. baseのhtml(js + css)を作成する。
1. mermaidの記述をasciidocのpassthrough+htmlに変換する : typescript
1. asciidoc→htmlの変換を行う : typescript
1. mermaid→svgの変換を行う : ブラウザ(webviewpanel)

という手順で処理を行うようにしました。
（これらの詳細はgithubのsrc/extension.tsに記載されています。）

以下のようなasciidocを処理する場合

```
:toc: top
:sectnums:
:toc-title: 目次
:chapter-label:

== シーケンスダイアグラムのサンプル

[mermaid]
....
sequenceDiagram
	クライアント-->>サーバ:要求;
    サーバ-->>クライアント:応答;
....

== テーブルのテスト
[options="header"]
,===
1,2,3
テスト11,テスト12,テスト13
テスト21,テスト22,テスト23
テスト31,テスト32,テスト33
,===

== クラスダイアグラムのサンプル

[mermaid]
....
classDiagram
BaseClass |>-- Server
BaseClass |>-- Client
BaseClass |>-- Util
....
```

1.baseとなるhtmlの作成(抜粋)
ファイルから読み込んだjsとcssをhtmlに直接埋め込んでいます。

```typescript
const editor = vscode.window.activeTextEditor;
const content = editor?.document.getText().toString();
const js = "" + fs.readFileSync(path.join(context.extensionPath, 'mermaid.min.js');
const css = "" + fs.readFileSync(path.join(context.extensionPath, 'asciidoctor.css'));

const html = ConvertLink(asciidoctor.convert(ConvertMermaid(content)));

webViewPanel.webview.html = `\
<!DOCTYPE html>\
<html lang="ja"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\
<script>\
${js}\
</script>\
<style>\
${css}\
</style>\
<body>\
${html}\
</body>\
</html>`;
```

2.の手順で[mermaid]のブロックを

```
//ConvertMermaid(content)でasciidocのpassthrough+mermaid用htmlに書き換えます

// 変換前
[mermaid]
....
sequenceDiagram
	クライアント-->>サーバ:要求;
    サーバ-->>クライアント:応答;
....

// 変換後
++++
<div class="mermaid">
sequenceDiagram
    クライアント-->>サーバ:要求;
    サーバ-->>クライアント:応答;
</div>
++++
```
この変換を行っておくとブラウザのレンダリング時にmermaidが実行されます。

3.asciidocのhtml変換

```typescript
// convert関数を使用してasciidoc→htmlに変換する
const html = asciidoctor.convert(content);
```

4.mermaid→svgの変換を行う

```typescript
// webViewPanel.webview.htmlにHTMLのテキストを入力するとWEBページのレンダリングが開始され
// HTMLの<script></script>に埋め込んだmermaidが動作しsvgへの変換が行われ図表が表示されます。
webViewPanel.webview.html = html;
```

## ハマった点
### asciidocの目次が日本語だとページ内リンクが機能しない
VSCodeのWebViewPanel特有の問題で、Chrome、FireFox、Edge等のブラウザでは問題が起きませんでした。

リンクがNGなHTMLの例

```html
<li><a href="#_テスト">1. テスト</a>
...
<h2 id="_テスト">1. テスト</h2>
```

リンクがOKなHTMLの例

```html
<li><a href="#_test">1. Test</a>
...
<h2 id="_test">1. Test</h2>
```

解決方法
asciidoctorでHTMLに変換後にリンクのIDをエスケープする変換を行うようにしました。
(%u30AF%u30A0のような形式に変換します)

```typescript
// リンクのエスケープ処理
function ConvertLink(str: string): string {
	str = str.replace(/href\="\#_(.*)?"/g,function(match, args){
		return `href="#_${escape(args)}"`;
	});
	str = str.replace(/id\="_(.*)?"/g,function(match, args){
		return `id="_${escape(args)}"`;
	});
	return str;
}
```

### VSCodeのエディタの右上にボタンが表示されない
"group": "navigation"の記述が必要でした。

package.json

```json
		"menus": {
			"editor/title": [
				{
					"command": "asciidocplugin.previewAsciiDoc",
					"when": "resourceLangId == asciidoc",
					"group": "navigation"
				}
			]
		},
```