概要
GitBucket は、GitHub のようにネットワーク上で Git リポジトリを共有できる Web サービスです。
- Apache-2.0 ライセンスで配布されている。
- Scala で開発されており、Java 17 実行環境があれば、起動可能。
- お試しであれば、内臓のH2データベースエンジンが利用できる。
- 実運用には、PostgreSQL や MariaDB などのリレーショナルデータベースサーバーの利用を推奨
- 非常に簡単にセルフホストが可能です。
今回は、GitBucket 標準のマークダウンレンダリング機能を置き換える試みです。
成果を yasumichi/gitbucket-markdown-enhanced: GitBucket Markdown Enhanced Plugin に公開しています。
Scala の利用経験が浅いので色々、ご教示いただけますと幸いです。
これまでの GitBucket プラグインへの関わり
gitbucket-drawio-plugin を導入しましたが、4年開発が止まっているせいか、うまく動作しなかったので改修しました。
目標は Visual Studio Code の Markdown Preview Enhanced
目標は、Visual Studio Code の Markdown Preview Enhanced 向けの markdown ファイルを軽易に Web で共有できる環境です。
実現できない機能もあるかと思いますが、できるだけ、近づけたいと考えます。
本家の gitbucket.core.view.Markdown の拡張を考えたが挫折
本家の gitbucket/src/main/scala/gitbucket/core/view/Markdown.scala を派生して強化するか、このクラスが利用している markedj を直接修正するか考えましたが、挫折しました。
flexmark-java との出会い
大してリサーチした訳ではないですが、Java の markdown ライブラリを探していた際に最近の commit もあって拡張機能が豊富な flexmark-java を見つけました。
flexmark-java を Scala から利用するためには、build.sbt に以下の行を加えます。
libraryDependencies += "com.vladsch.flexmark" % "flexmark-all" % "0.64.8"
gitbucket.core.plugin.Renderer の拡張
Renderer を開発するには、gitbucket.core.plugin.Renderer から派生したクラスで render メソッドをオーバーライドします。
素の flexmark-java を使う場合は、以下のようになります。
class MarkdownEnhancedRenderer extends Renderer {
def render(request: RenderRequest): Html = {
import request._
Html(toHtml(fileContent)(context))
}
def shutdown(): Unit = {
}
def toHtml(content: String)(implicit context: Context): String = {
val options = new MutableDataSet();
val parser = Parser.builder(options).build()
val renderer = HtmlRenderer.builder(options).build()
val document = parser.parse(content)
renderer.render(document)
}
}
gitbucket.core.plugin.Plugin の拡張
gitbucket.core.plugin.Plugin を継承し、前項で作成した renderer を initialize メソッドで登録します。
class Plugin extends gitbucket.core.plugin.Plugin {
override val pluginId: String = "gitbucket-markdown-enhanced"
override val pluginName: String = "GitBucket Markdown Enhanced Plugin"
override val description: String = "Rendering markdown files."
override val versions: List[Version] = List(
new Version("0.1.0")
)
private[this] var renderer: Option[MarkdownEnhancedRenderer] = None
override def initialize(registry: PluginRegistry, context: ServletContext, settings: SystemSettingsService.SystemSettings): Unit = {
val test = Try{ new MarkdownEnhancedRenderer() }
val mer = test.get
registry.addRenderer("md", mer)
renderer = Option(mer)
super.initialize(registry, context, settings)
}
override def shutdown(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Unit = {
renderer.foreach(r => r.shutdown())
}
}
flexmark-java の拡張機能を利用
拡張機能のコレクションを生成し、オプション Parser.EXTENSIONS
にセットします。
val extension: Seq[Extension] = Seq(
AnchorLinkExtension.create(),
EmojiExtension.create(),
FootnoteExtension.create(),
GitLabExtension.create(),
StrikethroughExtension.create(),
TablesExtension.create(),
TaskListExtension.create(),
TocExtension.create()
)
options.set(Parser.EXTENSIONS, extension.asJava)
この際、Scala のコレクションを Java のコレクションに変換する asJava
メソッドを使用するため、以下の import 文を追加する必要がありました。
import collection.JavaConverters._
AnchorLinkExtension
タイトルアンカーをリンクにする機能です。
GitBucket 本家とリンクの表示が異なるので同じようにできないか、調査中です。
EmojiExtension
flexmark-java/flexmark-ext-emoji/src/main/resources/EmojiReference.txt at master · vsch/flexmark-java で定義されている絵文字のショートカットが使えるようです。(設定次第で増やせるのか未確認)
:grinning:
のような記述で のような絵文字に変換されます。
FootnoteExtension
脚注を有効にする拡張機能です。
GitLabExtension
GitLab Flavoured Markdown を利用できるようにする拡張機能です。
特に以下の機能サポートを利用できるようになります。
ただし、これらを利用する場合、以下の CSS と JavaScript の追加が必要です。
<link rel="stylesheet" href="katex.min.css">
<script src="katex.min.js"></script>
<script src="mermaid.min.js"></script>
<script>
(function () {
document.addEventListener("DOMContentLoaded", function () {
var mathElems = document.getElementsByClassName("katex");
var elems = [];
for (const i in mathElems) {
if (mathElems.hasOwnProperty(i)) elems.push(mathElems[i]);
}
elems.forEach(elem => {
katex.render(elem.textContent, elem, { throwOnError: false, displayMode: elem.nodeName !== 'SPAN', });
});
});
})();
これを出力するため、 gitbucket.core.plugin.Plugin の javaScripts メソッドをオーバーライドします。
override def javaScripts(registry: PluginRegistry, context: ServletContext, settings: SystemSettingsService.SystemSettings): Seq[(String, String)] = {
val jsPath = settings.baseUrl.getOrElse(context.getContextPath) + "/plugin-assets/gme"
Seq(".*" -> s"""
|</script>
|<link rel='stylesheet' href='$jsPath/katex.min.css'>
|<script src="${jsPath}/katex.min.js">
|</script>
|<script src="${jsPath}/mermaid.min.js">
|</script>
|<script>
|(function () {
| document.addEventListener("DOMContentLoaded", function () {
| var mathElems = document.getElementsByClassName("katex");
| var elems = [];
| for (const i in mathElems) {
| if (mathElems.hasOwnProperty(i)) elems.push(mathElems[i]);
| }
| elems.forEach(elem => {
| katex.render(elem.textContent, elem, { throwOnError: false, displayMode: elem.nodeName !== 'SPAN', });
| });
| });
|})();
|""".stripMargin)
}
StrikethroughExtension
打消し線を有効にする拡張機能です。
TablesExtension
表の出力を制御する拡張機能です。
TaskListExtension
GitBucket 本家の機能でも実現されていますが、GitHub 風のタスクリストを扱うための拡張機能です。
TocExtension
目次(Table of contents) を出力できる拡張です。
[TOC]
上記の記述がある部分を目次に変換します。 levels
でどの階層までを目次に出力するか、制御できます。
参考文献
- vsch/flexmark-java: CommonMark/Markdown Java parser with source level AST. CommonMark 0.28, emulation of: pegdown, kramdown, markdown.pl, MultiMarkdown. With HTML to MD, MD to PDF, MD to DOCX conversion modules.
- 新Markdown導入の背景とその実装について | 株式会社ヌーラボ(Nulab inc.)
- flexmark-java で Markdown を HTML に変換する #Java - Qiita
- Java と Scala 間のコレクションの変換 | Collections | Scala Documentation
- sbt Reference Manual — ライブラリ依存性