0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitBucket Markdown Enhanced Plugin に vega および vega-lite サポートを追加しました(とりまJSONのみ)

Last updated at Posted at 2025-12-31

大晦日に何をやっているのか

言わないで…

言わないで…

さよならは…(これ以上は、●ASRAC に怒られる)

概要

GitBucket用のプラグイン GitBucket Markdown Enhanced Pluginを開発しています。

GitBucket Markdown Enhanced Plugin は、GitBucket 標準のマークダウンレンダリングエンジンを置き換えるプラグインです。

目標は、Visual Studio CodeMarkdown Preview Enhanced 向けの markdown ファイルを軽易に Web で共有できる環境です。

しばらく別のプラグインの開発をしていたため、久しぶりの更新です。

今回は、Markdown Preview Enhanced が対応している Vega and Vega-lite サポートを追加した話です。

今のところ、Markdown Preview Enhanced が対応している YAML1 形式には対応していません。JSON2 のみ対応しています。

前回の記事

機能の説明

vega あるいは vega-lite 用のコードブロックに専用の JSON を書くとグラフが表示されます。

vega-lite.png

以下のコードブロックが、上の画像に変換されます。

サンプルコード

```vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v6.json",
  "description": "A simple bar chart with embedded data.",
  "data": {
    "values": [
      {
        "a": "A",
        "b": 28
      },
      {
        "a": "B",
        "b": 55
      },
      {
        "a": "C",
        "b": 43
      },
      {
        "a": "D",
        "b": 91
      },
      {
        "a": "E",
        "b": 81
      },
      {
        "a": "F",
        "b": 53
      },
      {
        "a": "G",
        "b": 19
      },
      {
        "a": "H",
        "b": 87
      },
      {
        "a": "I",
        "b": 52
      }
    ]
  },
  "mark": "bar",
  "encoding": {
    "x": {
      "field": "a",
      "type": "ordinal"
    },
    "y": {
      "field": "b",
      "type": "quantitative"
    }
  }
}
```

JSON オブジェクトではなく、JSON 文字列を書くことに注意してください。プロパティ名をダブルクォーテーション等で囲む必要があります。

vega 関連のスクリプトファイルを LICENSE とともに同梱

専用のディレクトリ src/main/resources/gme/assets/vegaを掘り、以下のスクリプトを LICENSE とともに配置しました。

Plugin クラスの javaScripts メソッドに上記ファイルを読み込むための記述を追加

src/main/scala/Plugin.scala に以下の記述を追加しました。

src/main/scala/Plugin.scala
      |<script src="${jsPath}/vega/vega-6.2.0.js" type="text/javascript">
      |</script>
      |<script src="${jsPath}/vega/vega-lite-6.4.1.js" type="text/javascript">
      |</script>
      |<script src="${jsPath}/vega/vega-embed-7.0.2.js" type="text/javascript">
      |</script>

MarkdownEnhancedNodeRenderer に処理を追加

src/main/scala/io/github/yasumichi/gme/MarkdownEnhancedNodeRenderer.scala に専用のメソッドを追加しました。

src/main/scala/io/github/yasumichi/gme/MarkdownEnhancedNodeRenderer.scala
  private def renderVega(
      html: HtmlWriter,
      node: FencedCodeBlock,
      context: NodeRendererContext,
      language: String
  ): Unit = {
    html.withAttr().attr("id", s"vega-${vegaId}").tag("div")
    html.tag("/div")
    html.withAttr().attr("type", language).attr("class", "vega").attr("data-target", s"#vega-${vegaId}").tag("script")
    vegaId = vegaId + 1;
    html.rawIndentedPre(node.getContentChars())
    html.tag("/script")
  }

グラフを埋め込むための <div> タグと JSON を埋め込むための <script> タグを出力しています。

JavaScript として処理されないように type 属性を vage または vega-lite にしています。(WaveDrom 方式)

また、グラフを埋め込むための <div> タグを決定するために data-target 属性にセレクターを付与しています。

vegaId はクラスの private フィールドです。

このメソッドをコードブロックの処理を振り分けるところから、呼び出しています。

最終的に描画を行うスクリプト

src/main/resources/gme/assets/gme.js に最終的な描画を行う関数を追加しました。

src/main/resources/gme/assets/gme.js
    var renderVega = function() {
        return new Promise((resolve, reject) => {
            let vegaList = document.querySelectorAll('.vega');
            vegaList.forEach((node, index) => {
                let vegaId = node.getAttribute('data-target');
                try {
                    let vegaData = JSON.parse(node.textContent);
                    vegaEmbed(vegaId, vegaData);
                } catch (error) {
                    let node = document.querySelector(vegaId)
                    if (node) {
                        node.textContent = error.message;
                    }
                }
            });
            resolve();
        });
    }

一応、Promise を使って非同期処理にしています。

主な処理は以下の通りです。

  • class 属性が vega であるノードを探し、forEach で走査
    • ノードの data-target 属性の値を取得
    • ノードの textContent プロパティを JSON.parse() でオブジェクトに変換
    • vegaEmbed でターゲットノードにグラフを埋め込み

この関数を以下のそれぞれで呼び出しています。

  • DOMContentLoaded が発火した時(閲覧時)
  • リポジトリビューアーでファイル編集時にプレビューされた時
  • issue コメントなどを編集時にプレビューされた時

最後までお読みいただきありがとうございました。

今年も最後までお付き合いいただきありがとうございました。

それでは、よいお年を。

  1. YAML Ain't Markup Language (← Yet Another Markup Language) - YAML Ain’t Markup Language (YAML™) revision 1.2.2

  2. JavaScript Object Notation - RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?