現在のhighlight.jsではこのstateを引き回す書き方は非推奨となっており、今後廃止される予定です。
highlight.js というコードハイライトをするJavaScriptのライブラリーがあります。
このライブラリーで、「行ごと」にHTML要素を分割しつつハイライトする方法を説明します。
highilght.jsの基本的な使い方をおさらい
highlight.jsは基本的に関数を呼ぶだけでコードハイライトができるので便利です。
以下の例では "# Heading 1"
という文字列をMarkdownとしてハイライトしています。
import hljs from 'highlight.js' let result = hljs.highlight("markdown", "# Heading1") console.log(result.value) // '<span class="hljs-section"># Heading 1</span>'
highlight.js は '<span class="hljs-section"># Heading 1</span>'
のようなハイライト用のHTMLタグが埋められた結果を返します。
.hljs-section
クラスにCSSを当てることでハイライトができるというものです。
失敗例: 行を分けてからハイライトするとうまくいかない
画面の仕様により、表示するハイライト済みのコードを行ごとに区切りたいことがあります。 例えば以下のように、行番号を表示しつつコードを表示したいときなどがあります。
<table> <tr> <td>1</td> <td><span class="hljs-section"># Heading1</span></td> </tr> <tr> <td>2</td> <td>This is body</td> </tr> </table>
もちろん行番号を表示するコードの実装方法も様々ですが、画面や動作の都合上 <table>
で実装するとします。
このとき、肝心のコードの内容は # Heading 1
と This is body
に行ごとに区切る必要があります。
以下のように行ごとにハイライトするのは良くありません。
import hljs from 'highlight.js' let body = `# Heading 1 This is body \`\`\`python import this \`\`\` ` for (let row of body.split("\n")) { let result = hljs.highlight("markdown", row) console.log(result.value) }
理由は、複数行にまたがるシンタックス(Markdownならコードブロックなど)に対応できないからです。
上記の例だと```python 、 import this
、 ``` が別々に解釈されるので、サブブロック(import this
)のハイライトが効かなくなります。
回答: 状態を共有しながらハイライトする
highlight.jsの .highilght(...)
関数は第4引数に状態を渡せます。
ハイライトした結果の状態を引き継いで渡すことで、複数行にまたがるシンタックスでもうまく解釈されます。
function lineByLineHighilght (body) { let state = null return body.split("\n").map(function (row) { let result = hljs.highlight('markdown', row, true, state); state = result.top // result.topの状態を次に受け渡す return result.value + "<br/>" }) }
こうすることで行ごとに区切りつつ、行ごとにハイライトが効くようになります
(行ごとに毎度 <span class="...">...</span>
は正しく閉じられます)。