LoginSignup
59
44

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-12-22

はじめに

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

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

構文を定義する

textmate的構文定義は

  • .tmLanguage
  • .json
  • .plist

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

英語ですが公式のドキュメントがあるので,重要そうな部分だけを説明します。

概要

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

キーの説明

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

name

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

nameの命名規則

箇条書きで説明を書いていきますが,実際に使うのは箇条書きの項目をドット.で連結したものです。
たとえば

  • constant
    • numeric

となっていたら"name": "constant.numeric"とします。また全てを網羅しているわけではないので公式のドキュメント下の方も合わせてご覧ください。


  • 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
      クラスや関数定義のclassfunctionの部分に使います。
    • 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で()で囲うことによりグループを作った場合,
そのグループごとについてさらにパターンを適用できます。

やってみよう

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

var count: int = 10;

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

{
	"$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

参考リンク

Sublime Text2 言語パッケージ作成:構文定義
textmane manual - 12. Language Grammars

59
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
44