Typesafe Config(HOCON)のコードブロックにPrism.jsでシンタックスハイライトを当てる

Prism.jsはコードブロックにシンタックスハイライトを当てる便利なライブラリです。
Typesafe Configをハイライトする機能はサポートされていませんが、独自にルールを定義することによってシンタックスハイライトを適用することができます。

Typesafe Config とは?

https://github.com/lightbend/config

ScalaやJavaで使えるconfigライブラリです。
PlayFrameworkのプロジェクトでよく使います。

一般的には、HOCON(Human-Optimized Config Object Notation)形式で書かれます。

ハイライトする

冒頭で書いたように、Typesafe Configはサポートされていないのでハイライトルールを独自に定義します。
issueは出ていました。)

ハイライトルール(Grammar)

Prism.jsはハイライトルールを正規表現で定義します。
JSONを参考にHOCON用に拡張してみました。

全てのパターンで完璧にハイライトできるわけではありませんが、基本は問題なく動くと思います。

JavaScript
const hoconGrammar = { property: [/(?:[\w\-]|[^:\/\+.\[\]{}="$\s\r\n]+)/, /"(?:\\.|[^"\r\n]*)"/].map( r => ({ pattern: new RegExp(r.source + '(?=\\s*(?:[\\.:={]|\\+=))'), lookbehind: true, greedy: true, }), ), string: [ { pattern: /"""[\s\S]*?"""/, greedy: true }, { pattern: /(^|[^\\])"(?:\\.|[^"\\\r\n])*"/, lookbehind: true, greedy: true, }, ], comment: { pattern: /(\/\/|#).*/, greedy: true }, punctuation: /[\${}\[\],]/, operator: /[:=]/, keyword: { pattern: /(?:^|\s)include(?=\s)/, lookbehind: true, greedy: true, }, function: { pattern: /(?:^|\s)(file|url|classpath|required)(?=\()/, greedy: true, }, }

Node.jsで使う例

Node.js
import Prism from 'prismjs' const code = 'key = "value"' const result = Prism.highlight(code, hoconGrammar, 'hocon') console.log(result)
結果
<span class="token property">key</span> <span class="token operator">=</span> <span class="token string">"value"</span>

HTMLで使う例

HTML
<pre><code class="language-hocon"> key = "value" </code></pre> <script src="https://cdn.jsdelivr.net/npm/[email protected]/prism.min.js"></script> <script> Prism.languages.hocon = { // 省略 // hoconGrammarをここに書く } </script>

Prism.languagesを拡張してhoconを追加、codeタグのclassにlanguage-hoconを追加するとハイライトできます。

コードサンプル: https://github.com/murosan/prism-test/blob/main/docs/hocon.html
実際にハイライトした例: https://murosan.github.io/prism-test/hocon.html

サンプル

Typesafe
// コメント # コメント not.a.comment = [ "文字列の中はコメントではない" "//quoted", "#string" ]//comment arr=[1,2 3] "fo:o": { "a" : 42 }, "f$oo" : [null, true, false], "foo " : { "b" : 43 } "foo" : { "a" : 42, "b" : 1e5 } // a b c は有効なプロパティだけど、このGrammarだとハイライトされない.. // こういう書き方は普通しないので問題ないと思うが.. a b c : 42 "a b c" : 43 without_colon { a: 100 } // {が続く場合は:や=なしでも良い obj = { aaa.bbb = 100 ccc.ddd = [200 300] eee.fff { ggg.hhh: xxxx } } // Scalaのmultiline stringsみたいなやつが使える multiline = """" " aaa bbb """ // プロパティにもできる """ multilinekey """: val data-center-generic = { cluster-size = 6 } data-center-east = ${data-center-generic} { name = "ea:st" } path = [ /bin ] path = ${?path} [ /usr/bin ] path += /usr/local/bin path -= /usr/sbin // "path -" is a valid key included { include "another.conf" } ;;;|||¥¥¥--- = ok include file("aaa.conf") include url("file://bbb.conf") include classpath("ccc.conf") include required("ddd.conf")