概要
GitBucket用のプラグイン GitBucket Markdown Enhanced Pluginを開発しています。
今回は、Visual Studio Code のプラグイン Markdown Preview Enhancedがサポートしている WaveDrom のサポートを追加しました。
前回の記事
必要なスクリプトを同梱
src\main\resources\gme\assets\wavedrom ディレクトリを作成し、default.js と wavedrom.min.js を同梱しました。
必要なスクリプトを読み込む修正
src\main\scala\Plugin.scala の javaScripts メソッドを修正しました。
diff --git a/src/main/scala/Plugin.scala b/src/main/scala/Plugin.scala
index a221329..f29afc7 100644
--- a/src/main/scala/Plugin.scala
+++ b/src/main/scala/Plugin.scala
@@ -88,13 +88,17 @@ class Plugin extends gitbucket.core.plugin.Plugin {
|<link rel='stylesheet' href='$jsPath/admonition.css'>
|<link rel='stylesheet' href='$jsPath/katex.min.css'>
|<link rel='stylesheet' href='$jsPath/gme.css'>
- |<script src="${jsPath}/admonition.js">
+ |<script src="${jsPath}/admonition.js" type="text/javascript">
|</script>
- |<script src="${jsPath}/katex.min.js">
+ |<script src="${jsPath}/katex.min.js" type="text/javascript">
|</script>
- |<script src="${jsPath}/mermaid.min.js">
+ |<script src="${jsPath}/mermaid.min.js" type="text/javascript">
|</script>
- |<script src="${jsPath}/gme.js">
+ |<script src="${jsPath}/wavedrom/default.js" type="text/javascript">
+ |</script>
+ |<script src="${jsPath}/wavedrom/wavedrom.min.js" type="text/javascript">
+ |</script>
+ |<script src="${jsPath}/gme.js" type="text/javascript">
|""".stripMargin)
}
関係ない修正が入ってしまいましたが、肝となるのは次の行です。
|<script src="${jsPath}/wavedrom/default.js" type="text/javascript">
|</script>
|<script src="${jsPath}/wavedrom/wavedrom.min.js" type="text/javascript">
|</script>
WaveDrom のコードブロックを生成できるように既存の処理を修正
src\main\scala\io\github\yasumichi\gme\MarkdownEnhancedNodeRenderer.scala の renderFencedCodeBlock メソッド(旧名 render メソッド) を修正し、新しく作成した renderWaveDrom メソッドを呼び出すようにしました。
修正後の renderFencedCodeBlock メソッドは、次のコードになりました。
private def renderFencedCodeBlock(
node: FencedCodeBlock,
context: NodeRendererContext,
html: HtmlWriter
): Unit = {
val htmlOptions: HtmlRendererOptions = context.getHtmlOptions()
val language: BasedSequence =
node.getInfoDelimitedByAny(htmlOptions.languageDelimiterSet)
if (language.equals("plantuml")) {
renderPlantUML(html, node)
} else if (language.equals("wavedrom")) {
renderWaveDrom(html, node)
} else {
context.delegateRender()
}
}
上記で呼び出される renderWaveDrom メソッドは、次のコードです。
private def renderWaveDrom(html: HtmlWriter, node: FencedCodeBlock): Unit = {
var text = ""
var seqs = node.getContentLines().toArray()
for (i <- 0 to seqs.length - 1) text = text + seqs(i).toString()
logger.info(text)
html
.withAttr()
.attr("type", "WaveDrom")
.tag("script")
html.append(text)
html.tag("/script")
}
この段階で以下のようなマークダウンを書くと HTML への変換時に <script> ブロックが生成されるようになりました。
```wavedrom
{ signal : [
{ name: "clk", wave: "p......" },
{ name: "bus", wave: "x.34.5x", data: "head body tail" },
{ name: "wire", wave: "0.1..0." },
]}
```
↓
<script type="WaveDrom" id="InputJSON_0">{ signal : [
{ name: "clk", wave: "p......" },
{ name: "bus", wave: "x.34.5x", data: "head body tail" },
{ name: "wire", wave: "0.1..0." },
]}
</script>
WaveDrom.ProcessAll() を実行しても図形が描画されない…
この段階で開発者ツールを使用して、WaveDrom.ProcessAll() を実行しましたが、以下のようなエラーメッセージが表示され、図形を描画することができませんでした。
Uncaught TypeError: points.item(...).type.toLowerCase is not a function
exports https://cdnjs.cloudflare.com/ajax/libs/wavedrom/3.1.0/wavedrom.min.js:2
onload 省略
wavedrom.min.js:2:17034
exports https://cdnjs.cloudflare.com/ajax/libs/wavedrom/3.1.0/wavedrom.min.js:2
onload 省略
調査したところ、SVG 要素が混在している場合に当該エラーが発生することが分かりました。
WaveDrom の lib\process-all.js を以下のように修正して、ビルドしたバージョンを使用することにしました。
diff --git a/lib/process-all.js b/lib/process-all.js
index 912847f..0f90c39 100644
--- a/lib/process-all.js
+++ b/lib/process-all.js
@@ -9,7 +9,7 @@ function processAll () {
let index = 0; // actual number of valid anchor
const points = document.querySelectorAll('*');
for (let i = 0; i < points.length; i++) {
- if (points.item(i).type && points.item(i).type.toLowerCase() === 'wavedrom') {
+ if (points.item(i).type && typeof(points.item(i).type) == "string" && points.item(i).type.toLowerCase() === 'wavedrom') {
points.item(i).setAttribute('id', 'InputJSON_' + index);
const node0 = document.createElement('div');
問題になったノードを調べたところ、type プロパティが文字列ではなく SVGAnimatedEnumeration { baseVal: 1, animVal: 1 } になっていたため、上記のように修正しました。
本件、本家に issue と Pull Request を送りました。
WaveDrom.ProcessAll() fails when SVG elements are mixed. · Issue #442 · wavedrom/wavedrom
Excludes SVG elements by yasumichi · Pull Request #441 · wavedrom/wavedrom
Reject されないと良いのですが…
WaveDrom.ProcessAll() を自動で呼ぶように修正
src\main\resources\gme\assets\gme.js を修正しました。
以下、抜粋です。
document.addEventListener("DOMContentLoaded", function(){
var preview = document.querySelector("#preview");
if (preview) {
const config = { attributes: false, childList: true, subtree: false };
const observer = new MutationObserver((mutations) => {
mutations.forEach(async (mutation) => {
if (mutation.addedNodes.length == 1) {
observer.disconnect();
renderKatex();
await mermaid.run();
WaveDrom.ProcessAll();
observer.observe(preview, config);
}
});
});
observer.observe(preview, config);
}
renderKatex();
WaveDrom.ProcessAll();
});
以上で GitBucket で WaveDrom の図が表示できるようになりました。
スクリーンショットを貼ります。
