概要
GitBucket用のプラグイン GitBucket Markdown Enhanced Pluginを開発しています。
GitBucket Markdown Enhanced Plugin は、GitBucket 標準のマークダウンレンダリングエンジンを置き換えるプラグインです。
目標は、Visual Studio Code の Markdown Preview Enhanced 向けの markdown ファイルを軽易に Web で共有できる環境です。
今回は、GitBucket の標準機能に搭載されているシンタックスハイライトを有効にします。
前回の記事
flexmark-java のコードハイライトに対する立場を検索してみた
度々、Issue が投稿されるようだが、代表的な issue を挙げます。
Is the library support code highlight? · Issue #183 · vsch/flexmark-java
No, this library cannot highlight your code (jet?). But you could use something like prism.js if your app is for web.
flexmark-java としては、シンタックスハイライトを行えないが、prism.js のようなものを使えば実現できるよと。
GitBucket では、シンタックスハイライトに highlight.js を使用しており、マークダウンレンダラーを差し替えても highlight.js に関連するスクリプトが読み込まれるようになっています。
クライアント負荷やリポジトリビューアーのファイルビューとの整合を考えると prism.js を使うよりも既存機能を使った方が良さそうです。
生成されるコードブロックの違い
GitBucket では、シンタックスハイライトに highlight.js を使用しており、マークダウンレンダラーを差し替えても highlight.js
比較に使ったマークダウンは、次のとおりです。
```scala
private def renderFencedCodeBlock(
node: FencedCodeBlock,
context: NodeRendererContext,
html: HtmlWriter
): Unit = {
val htmlOptions: HtmlRendererOptions = context.getHtmlOptions()
val language: BasedSequence =
node.getInfoDelimitedByAny(htmlOptions.languageDelimiterSet)
val info: String = node.getInfo().toString()
logger.debug("FencedCodeBlock getInfo: " + node.getInfo().toString())
if (language.equals("plantuml") || language.equals("puml")) {
renderPlantUML(html, node)
} else if (language.equals("wavedrom")) {
renderWaveDrom(html, node)
} else if (language.equals("dot") || language.equals("viz")) {
renderDot(html, node, info)
} else {
context.delegateRender()
}
}
```
このマークダウンを元に GitBucket の標準機能が生成する HTML と GitBucket Markdown Enhanced Plugin が生成する HTML を比較してみます。
標準
<pre class="prettyprint lang-scala"> private def renderFencedCodeBlock(
node: FencedCodeBlock,
context: NodeRendererContext,
html: HtmlWriter
): Unit = {
val htmlOptions: HtmlRendererOptions = context.getHtmlOptions()
val language: BasedSequence =
node.getInfoDelimitedByAny(htmlOptions.languageDelimiterSet)
val info: String = node.getInfo().toString()
logger.debug("FencedCodeBlock getInfo: " + node.getInfo().toString())
if (language.equals("plantuml") || language.equals("puml")) {
renderPlantUML(html, node)
} else if (language.equals("wavedrom")) {
renderWaveDrom(html, node)
} else if (language.equals("dot") || language.equals("viz")) {
renderDot(html, node, info)
} else {
context.delegateRender()
}
}</pre>
GitBucket Markdown Enhanced Plugin
<pre><code class="language-scala"> private def renderFencedCodeBlock(
node: FencedCodeBlock,
context: NodeRendererContext,
html: HtmlWriter
): Unit = {
val htmlOptions: HtmlRendererOptions = context.getHtmlOptions()
val language: BasedSequence =
node.getInfoDelimitedByAny(htmlOptions.languageDelimiterSet)
val info: String = node.getInfo().toString()
logger.debug("FencedCodeBlock getInfo: " + node.getInfo().toString())
if (language.equals("plantuml") || language.equals("puml")) {
renderPlantUML(html, node)
} else if (language.equals("wavedrom")) {
renderWaveDrom(html, node)
} else if (language.equals("dot") || language.equals("viz")) {
renderDot(html, node, info)
} else {
context.delegateRender()
}
}
</code></pre>
違い
- 標準機能では prettyprint というクラスと pre 要素に直接コードが記述されている。
- pre 要素に prettyprint というクラスとプログラミング言語を表すクラス
lang-言語名というクラスが指定されている。
- pre 要素に prettyprint というクラスとプログラミング言語を表すクラス
- GitBucket Markdown Enhanced Plugin では、pre 要素の子として code 要素がある。
- pre 要素ではなく code 要素にクラスとプログラミング言語を表す
language-言語名というクラスが指定されている。
- pre 要素ではなく code 要素にクラスとプログラミング言語を表す
GitBucket Markdown Enhanced Plugin 側で標準機能に合わせた出力に修正していくことにします。
修正ソース
src\main\scala\io\github\yasumichi\gme\MarkdownEnhancedNodeRenderer.scala(修正前)
今まで GitBucket Markdown Enhanced Plugin で独自の拡張を行う言語名以外は、flexmark-java の既存のレンダラーに委譲していましたが、そこも独自実装することにします。
ただ、独自で処理しない mermaid は、他のレンダラー(GitLabExtension)に委譲するようにしています。
private def renderFencedCodeBlock(
node: FencedCodeBlock,
context: NodeRendererContext,
html: HtmlWriter
): Unit = {
val htmlOptions: HtmlRendererOptions = context.getHtmlOptions()
val language: BasedSequence =
node.getInfoDelimitedByAny(htmlOptions.languageDelimiterSet)
val info: String = node.getInfo().toString()
logger.debug("FencedCodeBlock getInfo: " + node.getInfo().toString())
if (language.equals("plantuml") || language.equals("puml")) {
renderPlantUML(html, node)
} else if (language.equals("wavedrom")) {
renderWaveDrom(html, node)
} else if (language.equals("dot") || language.equals("viz")) {
renderDot(html, node, info)
} else if (language.equals("mermaid")) {
context.delegateRender()
} else {
renderPrittyPrint(html, node, context, language.toString())
}
}
最後の else 節で呼び出している renderPrittyPrint メソッドは、次のとおりです。
private def renderPrittyPrint(
html: HtmlWriter,
node: FencedCodeBlock,
context: NodeRendererContext,
language: String
): Unit = {
html
.withAttr()
.attr("class", s"prettyprint lang-${language}")
.tag("pre")
html.rawIndentedPre(node.getContentChars().toString())
html.tag("/pre")
}
html.rawIndentedPre(node.getContentChars().toString()) にたどり着くまでが苦労しました。
これまで使用してきた HtmlWriter クラス1 の append や text などのメソッドでは、行頭のインデントが削除されてしまうのです。
スクリーンショット
ここまでの修正でスクリーンショットのとおり、GitBucket 標準のシンタックスハイライトが適用されるようになりました。
入手先
Releases · yasumichi/gitbucket-markdown-enhanced から、最新バージョンを入手できます。
-
正確には基本クラス HtmlAppendableBase ↩
