Visual Studio CodeやAtomのシンタックスハイライト拡張機能を作る

## はじめに

Visual Studio CodeやAtomに言語のシンタックスハイライトを加えるにはtextmateというエディターが採用している記法で構文を定義します。

なぜtextmateの方法が採用されているのかは知らないのですが，どういうわけか広く利用されているようです。

## 構文を定義する
textmate的構文定義は

- `.tmLanguage`
- `.json`
- `.plist`

など様々な方法で記述できるようです。
個人的にはJSON版が扱いやすそうです。

英語ですが[公式のドキュメント](https://manual.macromates.com/en/language_grammars)があるので，重要そうな部分だけを説明します。

### 概要
構文は再帰的に定義できます。
あるブロックを認識してそのブロックは3つの構文に分解され最初の構文は別の構文によって処理される...のように記述できます。
**定数**を**数値定数**と**文字列定数**に分けるなど，できるだけ細かい単位で構文を作っておくと
`include`機能を使って使い回しが効きますし，見通しがよくなります。

### キーの説明
様々なキーを使って構文を定義します。

#### name
命名規則に従った名前をつけることで，「ここは関数名である」「ここはコメントである」「ここは文字列定数である」ということを指定します。Visual Studio Codeのようなエディタはこの情報をもとにしてテーマから指定に相当する色を決定し，シンタックスハイライト(色分け)を行うというわけです。

#### nameの命名規則
箇条書きで説明を書いていきますが，実際に使うのは箇条書きの項目をドット`.`で連結したものです。
たとえば

- constant
    - numeric

となっていたら`"name": "constant.numeric"`とします。また全てを網羅しているわけではないので[公式のドキュメント](https://manual.macromates.com/en/language_grammars)の**下の方**も合わせてご覧ください。

---

- comment
    - line
        - double-dash
        `//`で始まるコメントを表します
    - block
    `/* */`で囲むようなコメントブロックを表します。

- constant
    - numeric
    数値の定数を表します。 例: `42`
    - language
    その言語で特別に使う語を表します。 例: `true, nill`

- entity
    - name
        - function
        関数定義における関数の名前を表します。関数を呼び出すときのものではありません。

- invalid
    - illegal
    その言語においてありえない記述を表します。JSONでキーがダブルクォーテーションで囲われていない場合などに使います。
- keyword
    - control
    `continue, while, return`などフローの制御に使う語を表します。
    - operator
    演算子を表します。
- markup
    - italic
    イタリック体にしたい箇所に使います。
- storage
    - type
    クラスや関数定義の`class`や`function`の部分に使います。
    - modifiers
    `static, final, abstract`などに使います。
- string
    - quoted
        - double
        ダブルクォーテーション`"`で囲われた文字列を表します。
    - regexp
    正規表現を表します。例: `/[0-9]+/`
- support
フレームワークやライブラリで提供されるものはsupportで表現するようです。
    - function
    関数の呼び出しにおける関数名を表します。
`entity.name.function`との違いに注意。
- variable
    - parameter
    パラメーターとして宣言された変数を表します。
    - language
    その言語で予約されている変数を表します。例: `this, super, self`

#### match
正規表現でマッチさせたいパターンを入れます。
例`(\\b(true|false)\\b)`

#### patterns
マッチさせるパターンの候補(複数可)です。
上から順にマッチングが試行されます。
パターンが1つの場合は1つだけでよいです。

#### captures
matchで`()`で囲うことによりグループを作った場合，
そのグループごとについてさらにパターンを適用できます。

## やってみよう
ここまで長々と書きましたが，実際の例を見た方が分かりやすいと思います。
上で説明していないキーなどが登場しますが雰囲気はつかめると思います。
ここでは以下の変数宣言の構文定義の例を挙げます。

```swift
var count: int = 10;
```

スタート地点は**変数定義**(var_declaration)です。
**変数定義**の中で`(\\S+)\\s+(\\S+):\\s+(\\S+)\\s+=\\s+(\\S+);`という正規表現で4つの部分(**var**，**変数名**，**変数の型**，**定数**)に分けています。
例えば先頭の**var**は`name`に`storage.type.example`を指定しています。
また，最後の**定数**は**数値定数**(constant_number)と**文字列定数**(constant_string)で認識を試行します。
例の場合最後の**定数**は10なので**数値定数**で認識されることになります。**数値定数**の`name`には`constant.numeric.example`を指定しています。

```json
{
	"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
	"name": "",
	"patterns": [
		{
			"include": "#var_declaration"
		}
	],
	"repository": {
		"var_declaration": {
			"patterns": [
				{
					"match": "(\\S+)\\s+(\\S+):\\s+(\\S+)\\s+=\\s+(\\S+);",
					"captures": {
						"1": {"patterns": [{"include": "#var"}]},
						"2": {"patterns": [{"include": "#var_name"}]},
						"3": {"patterns": [{"include": "#var_type"}]},
						"4": {"patterns": [{"include": "#constant"}]}
					}
				}
			],
			"repository": {
				"var": {
					"patterns": [
						{
							"name" : "storage.type.example",
							"match": "var"
						}
					]
				},
				"var_name": {
					"patterns": [
						{
							"name": "variable.other.example",
							"match": "[0-9a-zA-Z]+"
						}
					]
				},
				"var_type": {
					"patterns": [
						{
							"name": "storage.type.example",
							"match": "\\b(int|double|float)\\b"
						}
					]
				},
				"constant": {
					"patterns": [
						{"include": "#constant_number"},
						{"include": "#constant_string"}
					],
					"repository": {
						"constant_number": {
							"patterns": [
								{
									"name": "constant.numeric.example",
									"match": "-?[0-9]+"
								}
							]
						},
						"constant_string": {
							"patterns": [
								{
									"name": "constant.character.example",
									"begin": "\"",
									"end": "\""
								}
							]
						}
					}
				}
			}
		}
	},
	"scopeName": "source.example"
}
```

ちなみに結果はこんな感じになります。きれいに色がついていますね。
![image](https://qiita-image-store.s3.amazonaws.com/0/56635/c7320498-4d93-bc34-c265-39a625293172.png)



## 参考リンク
[Sublime Text2 言語パッケージ作成：構文定義](http://qiita.com/makopo/items/64531b7372b450a7c3ee)
[textmane manual - 12. Language Grammars](http://manual.macromates.com/en/language_grammars)