プログラミングなどに関する、ひらう子のブログです

CodeMirrorで独自言語のシンタックスハイライトを使う②

前回、CodeMirrorの導入を行いました。今回は、独自言語のハイライトの骨子です。


CodeMirrorプロジェクト内の【/mode】ディレクトリには、言語ごとにディレクトリが置いてあって、中には、ハイライトが定義されているjsファイルが置いてあります。ひとまず、【/mode/css/css.js】を見てみましょう。

CodeMirror/css.js at master · codemirror/CodeMirror · GitHub

スクリプトでは基本的に、即時関数の中で変数や関数を定義したりと色々やっているのですが、その中でも核となるのは、CodeMirror.defineMode()の実行です。(CodeMirror.defineMIMEの実行もされていますが、こちらはあくまで単純にHTML内でハイライトを行うだけなら必要ない模様です。)

CodeMirror.defineMode()は、第一引数にモード名文字列("css"など)、第二引数に、実際の言語モードの内容を記した辞書オブジェクトを取ります。【css.js】の今のバージョンにおいては、defineMode()の第二引数は辞書オブジェクトを返す即時関数で記述されていますね。

そして、辞書オブジェクトの'token'プロパティに設定された関数が、実際にハイライトを行う関数です。


'token'プロパティの関数(以下、token関数と記述します)は、CodeMirrorのスクリプトがハイライトを実際に行う際に順次呼び出されます。

第一引数streamは、色分けすべきコードを読み出したり現在の読み出し位置を前後に動かしたりできるストリームです。
第二引数stateは、token関数がハイライト作業中に何度も呼ばれるにあたって、メモとして利用できる辞書オブジェクトです。

大まかな流れを説明すると、ハイライト作業が始まると、まずCodeMirrorが、token関数が呼び出します。最初の呼び出し時点で、streamの読み出し位置はコードの最初です。

token関数は、streamの位置を調整してトークン名の文字列をreturnで返します。するとCodeMirrorは、token関数が呼ばれた時点の読み出し位置から関数がreturnした際のstreamの読み出し位置までを一つのトークンとして認識し、返されたトークン名をつけます。そして、またすぐにtoken関数が呼ばれます。その際streamの読み出し位置は、先ほどreturnした時のままですので、再度位置を調整してreturnします。この繰り返しにより、CodeMirrorはハイライトすべきトークンの解析を行います。

実際にハイライトする際には、トークン名に最初に"cm-"をつけたものが、HTML内のそのトークンの部分にクラスとして付与されますので、CSSで色を付けることで、ハイライトが行えるというわけです。

streamには、eat()やmatch()といったメソッドがあります。match()は「読み出し位置に指定した単語があれば読み出し位置を進める」というメソッドですので、調整はこちらで行うことが多いでしょう。この記事では大まかにしか説明しませんので、詳しくは、以下リンクを参照してください。

CodeMirror: User Manual

ちなみに上で述べたとおり、token関数第二引数のstateは、メモとして使えます。なので、トークン識別を行うための文脈情報をこちらに記録したりして使うことが出来ます。stateの初期状態は、defineMode関数に渡される辞書オブジェクト内の、'startState'プロパティに設定された関数の返り値が使われます。使う際には、こちらで初期状態を設定しましょう。


そんな感じでハイライトを定義するスクリプトを書いたら、あとはそれをHTML内に読み込んでやれば、CodeMirror.colorize()関数やCodeMirror()を使って、ハイライトを実際に使うことが出来ます。自分でつけたモード名文字列を指定して、これらを読み出せば、ハイライトが出来るはずです。