ホーム
プロフィール
製作物
前の記事
IMathLibリファレンス
次の記事
JavaScriptによるスムーズスクロール
投稿日時:2020/01/23 00:21、最終更新日時:2020/01/26 15:13

ISyntaxの使い方

  • pocket
  • はてなブックマーク

はじめに

 こちらで公開されているJavaScriptから利用が可能なシンタックスハイライターISyntaxの紹介および使い方について記述する。

 私自身はWeb系に詳しくないため動作環境については明言できない。Google ChromeとFirefox、Microsoft Edgeの最新版では動作した。

利用方法

ファイルの読み込み

 ISyntaxを最低限利用するために必要なファイルはisyntax.jsとisyntax.cssの2つのみである。そのため、最低限の機能の利用に関しては

<link rel="stylesheet" href="isyntax/isyntax.css">           
<script type="text/javascript" src="isyntax/isyntax.js"></script>

のように記述するだけで十分である。ハイライト処理に関してはDOMContentLoadedイベントにより実行される。

ソースコードの記述

 ソースコードを記述するには

<pre class="isyntax_code-言語のシンボル名">
ソースコードの中身
</pre>

のように記述するだけである。標準ではテキストファイル用のシンボル名textのみが割り当てられている。
 ISyntaxでは拡張パッケージとして言語ごとのJavaScriptで記述されたパッケージの読み込みを行う手段が設けられているが、ほとんどの場合拡張子を除いたファイル名がそのまま言語のシンボル名とならなければならない。例えば、ISyntaxが標準で提供しているC++用の拡張パッケージcpp.jsではシンボル名はcppであり、

<pre class="isyntax_code-cpp">
#include &amp;lt;iostream&amp;gt;

int main() {
	std::cout &amp;lt;&amp;lt; &amp;quot;Hello, World!&amp;quot; &amp;lt;&amp;lt; std::endl;
	return 0;
}
</pre>

と記述すれば

#include <iostream>

int main() {
	std::cout << "Hello, World!" << std::endl;
	return 0;
}

と表示される。

行番号の表示

 ソースコードに対して行番号を表示したいとき、

<pre class="isyntax_code-cpp line_numbers">
#include &amp;lt;iostream&amp;gt;

int main() {
	std::cout &amp;lt;&amp;lt; &amp;quot;Hello, World!&amp;quot; &amp;lt;&amp;lt; std::endl;
	return 0;
}
</pre>

と記述すれば

#include <iostream>

int main() {
	std::cout << "Hello, World!" << std::endl;
	return 0;
}

と表示される。行番号に対してオフセットを設けたいならば、

<pre class="isyntax_code-cpp line_numbers" data-start="10">
#include &amp;lt;iostream&amp;gt;

int main() {
	std::cout &amp;lt;&amp;lt; &amp;quot;Hello, World!&amp;quot; &amp;lt;&amp;lt; std::endl;
	return 0;
}
</pre>

のようにデータ属性を設けることで

#include <iostream>

int main() {
	std::cout << "Hello, World!" << std::endl;
	return 0;
}

となる。

タイトルの表示

 ソースコードに対してタイトルを与えたいとき、

<pre class="isyntax_code-cpp" data-start="10">
#include &amp;lt;iostream&amp;gt;

int main() {
	std::cout &amp;lt;&amp;lt; &amp;quot;Hello, World!&amp;quot; &amp;lt;&amp;lt; std::endl;
	return 0;
}
</pre>

のようにデータ属性を設けることで

#include <iostream>

int main() {
	std::cout << "Hello, World!" << std::endl;
	return 0;
}

となる。

ファイルの静的読み込みと動的読み込み

 最初にisyntax.jsとisyntax.cssの読み込みについて述べたが、ここでは言語ごとの拡張パッケージについいて述べる。

静的読み込み

 あらかじめISyntaxで利用する言語が既知であったりするとき、例えばC++の拡張パッケージであれば

<link rel="stylesheet" href="isyntax/isyntax.css">           
<script type="text/javascript" src="isyntax/isyntax.js"></script>
<script type="text/javascript" src="isyntax/cpp.js"></script>

のようにisyntax.jsの後にcpp.jsを読み込むことでISyntaxでC++が利用できる。

動的読み込み

 例えば、

<pre class="isyntax_code-cpp">
#include &amp;lt;iostream&amp;gt;

int main() {
	std::cout &amp;lt;&amp;lt; &amp;quot;Hello, World!&amp;quot; &amp;lt;&amp;lt; std::endl;
	return 0;
}
</pre>

と記述されていて、C++の拡張パッケージcpp.jsが読み込まれていないとき、シンボル名cppに対応する拡張パッケージcpp.jsがisyntax.jsと同じディレクトリからXMLHttpRequestにより読み込まれ、ISyntaxによりC++が利用できる。ただし、パッケージが存在しなかったり読み込みが失敗したときは何も表示されなくなることに注意しなければならない

API

ISyntax.init(element = document)

 全てのelementに属する.language-*をフェッチしてハイライト処理を行う。

ISyntax.escape_html(string)

 文字列stringに含まれる&<>"'をHTMLエスケープ文字へと変換して、変換後の文字列を返す。これは開発者向けの関数である。

ISyntax.search_unescape(string, c, offset = 0)

 文字列stringから前の文字がバックスラッシュ\でない文字cを位置offsetから探索し、その位置を返す。これと同等の操作は

string.substr(offset).search(/(?<!\\)c/g);

により実現できるが、ES2018で追加された機能であるということもあり、ISyntax.search_unescapeを与えている。これは開発者向けの関数である。

ユーザによる拡張(開発者向け)

 ここからはユーザがISyntaxを拡張して用いるための方法について示す。

ISyntaxで独自言語を利用する

 ISyntaxではいくつかの言語をサポートしているが、多くの言語をサポートすることについては管理人の技術力的に大変困難なことである。そこで、新しい言語をISyntaxで容易に追加できる仕組みを提供する。その一例がファイルの動的読み込みである。
 以下では基本的な流れに沿って示していく。

シンボル名を決定する

 まずは言語を示すシンボル名を決定する。言語名そのままでもいいし、Javascriptをjsと略したものでもいい。しかし、シンボル名は重複してはならない

シンボル名.jsファイルを作成する

 シンボル名.jsの空ファイルを作成したら、その中身に

	ISyntax.シンボル名 = ISyntax.シンボル名 || {};
	ISyntax.シンボル名.language = '言語名';

	ISyntax.シンボル名.init = function(line) {};

といった記述をする。これはISyntax.シンボル名といった名前空間を作成し、ISyntax.シンボル名.languageで表示される言語名を定義、ISyntax.シンボル名.initは引数lineに対してハイライトを適用する関数である。
 lineは行の解析のための配列であり、

line = [現在操作対象の行番号, ソースコードの各行が格納されている配列, 行の解析のためのバッファ];

のような構成をしている。ISyntax.シンボル名.initに渡されるline

line = [0, ソースコードの各行のテキストが格納されている配列, ''];

である。このとき、line[1]に各行の解析結果のテキストが格納されるように解析をしなければならない。
 例えば、HTML用の拡張パッケージhtml.jsのバージョン0.1.0では以下のような大幅に手抜きな解析をしている。

/* ISyntax HTML 0.1.0 */

	ISyntax.html = ISyntax.html || {};
	ISyntax.html.language = 'HTML';

	ISyntax.html.init = function(line) {
		for (let n = line[1].length; line[0] < n; ++line[0]) {
			// タグ<~>を囲っているだけ
			line[1][line[0]] = line[1][line[0]].replace(/\<(.*?)\>/g
				, function (match) {
					return '<span class="keyword">' + ISyntax.escape_html(match) + '</span>';
				});
		}
	};

 基本的には現在操作対象である行をline[2]にコピーしてline[1][line[0]]を空にする。そして、line[2]を解析していき、解析済みのテキストをline[1][line[0]]に追記していくといった流れとなる。単純な数値や識別子であればこれで十分であるが、より正確な解析にはトークンの先読みや後読みが必要となるため、lineに対して要素を追加することもあるだろう。しかしながら、ほとんどの場合、シンタックスハイライトにはそこまでの正確さは求められない。もちろん、JavaScriptの正規表現リテラルのようにトークンの後読みをしなければ解析できない対象はする必要がある。
 また、シンタックスハイライトの結果のDOMツリーを直接構成する可能性も考えられるが、isyntax.jsのバージョン0.9.0では結果がDOMツリーの場合は対応はしていない。しかしながら、今後の開発によりISyntax.シンボル名.initでブーリアン型の戻り値が存在して、それがtrueならばline[1]の中身をElementオブジェクトとして扱う可能性があるだろう


前の記事
IMathLibリファレンス
次の記事
JavaScriptによるスムーズスクロール

コメント欄(詳細な記述方法についてはこちら)

この記事ではコメントが禁止されています