Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした