目次
1. はじめに ― 自作シンタックスハイライトの意義
みなさまは、独自ドメイン言語(DSL)を VSCode で編集するとき、キーワードもコメントも一様に灰色で表示されている場面に遭遇したことはありませんか。私が業務で扱う TPG(Test Program Generator)という産業機器向けスクリプト言語も、そのままでは読みづらく、ちょっとした入力ミスを見落としやすい状況でした。
本記事では「まずは最低限の色分けを実現する」という現実的な目標を掲げ、VSCode 拡張としてシンタックスハイライトを実装する手順を順を追ってご案内いたします。専門用語はできる限りかみ砕き、手順ごとに背景を説明しますので、拡張開発が初めての方でも安心して読み進めていただけます。
2. 開発環境の準備 ― 必要ツールをそろえる
拡張開発に必要となる主なツールは以下の三つです。
- Node.js(LTS 版推奨)
- Yeoman & VSCode 拡張ジェネレータ(
generator-code
) - Visual Studio Code 本体
インストールがお済みでない場合は、下記コマンドを参考にセットアップしてください。
# Node.js は公式サイトからインストール済みと仮定
npm install -g yo generator-code # Yeoman と拡張ジェネレータを全局インストール
code --version # VSCode が正しくインストールされているか確認
公式ドキュメントへのリンクを併記いたします。
- Node.js: https://nodejs.org/
- generator-code: https://github.com/microsoft/vscode-generator-code
- VSCode Extension API: https://code.visualstudio.com/api
3. 雛形を作成する ― yo code
ウィザードを一歩ずつ進める
まずはターミナルで次のコマンドを実行し、拡張テンプレートを生成します。
yo code
ウィザードでは複数の入力が求められます。代表的な項目と入力例、そして考慮事項を以下の表にまとめました。必要に応じてご自身のプロジェクトに合わせて変更してください。
質問項目 | 入力例 | ポイント・補足説明 |
---|---|---|
Extension type | New Language | ハイライト用拡張を作成する場合はこれを選択します。 |
Extension name | TPG Syntax Highlight | VSCode の拡張一覧に表示される名称です。 |
Identifier | tpg-syntax-highlight | npm 公開を想定し、短くわかりやすい英小文字+ハイフン推奨。 |
Description | (空欄でも可) | 後ほど package.json で加筆できます。 |
Language id | tpg | VSCode が内部で扱う言語 ID。拡張子とは別概念です。 |
Language name | Test Programming Generator | ステータスバーなどに表示されるフレンドリーな名称。 |
File extensions | .tpg | 複数ある場合はカンマ区切りで列挙してください。 |
Scope name | source.tpg | 後述する scopeName と一致させます。 |
Initialize git repo | Yes | Git 管理が不要な場合は No でも構いません。 |
ウィザード完了後、以下のようなディレクトリが生成されます(抜粋)。
tpg-syntax-highlight/
| package.json
| README.md
| language-configuration.json
|
+---syntaxes
| tpg.tmLanguage.json
|
+---.vscode
launch.json
ここまでで「骨格」は完成しました。次章からはハイライトの本体である TextMate Grammar を作成していきます。
4. TextMate Grammar を理解する ― tpg.tmLanguage.json
の書き方
VSCode は TextMate 形式の文法定義を用いてトークン(キーワードや文字列など)へ色を適用します。ポイントは次の三要素です。
JSON キー | 役割 | 記述例 |
scopeName | 「この文法の全体名」を宣言 | source.tpg |
patterns | 上から順に評価されるルールの入口 | { "include": "#comment" } など |
repository | ラベルごとの詳細な正規表現を格納 | comment , string など |
4.1 ラベル設計 ― “何に色を付けるか” を決める
まずは対象言語(TPG)において強調したいトークンを洗い出しましょう。最低限、以下 7 種類があると実務で困りません。
ラベル | 主な対象 | 推奨スコープ例 |
comment | /* … */ ブロックコメント、行頭 ; コメント | comment.block , comment.line |
string | シングルクォート文字列、計測器 %NN エスケープ付き文字列 | string.quoted.single |
number | 10 / 2 / 8 / 16 進数リテラル、実数リテラル | constant.numeric |
keyword | 制御構文 (IF など)、データ型 (FLOAT など) | keyword.control , storage.type |
operator | !! , .AND. , >= など多種多様な演算子 | keyword.operator |
label | 行頭のジャンプ先 START_LOOP: | entity.name.label |
identifier | 予約語を除くすべての識別子 | variable.other |
4.2 例示 ― コメント付き最小構成 JSON
以下は 動作確認用の最小実装 です。実務では正規表現をさらに細分化すると保守しやすくなります。
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/v1.0.0/tmlanguage.json",
"name": "Teradyne TPG",
"scopeName": "source.tpg",
"patterns": [
{ "include": "#comment" },
{ "include": "#string" },
{ "include": "#number" },
{ "include": "#keyword" },
{ "include": "#operator" },
{ "include": "#label" },
{ "include": "#identifier" }
],
"repository": {
"comment": {
"patterns": [
{ "name": "comment.line.semicolon.tpg", "match": ";.*$" },
{
"name": "comment.block.tpg",
"begin": "/\\*", // `/*` にマッチ
"end": "\\*/", // `*/` で閉じる
"patterns": [ { "include": "#comment" } ] // ネスト対応
}
]
},
"string": {
"name": "string.quoted.single.tpg",
"begin": "'",
"end": "'",
"patterns": [
{
"match": "%%|%[0-9A-Fa-f]{2}",
"name": "constant.character.escape.tpg"
}
]
},
"number": {
"patterns": [
{ "match": "\\b[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?\\b", "name": "constant.numeric.decimal.tpg" },
{ "match": "\\bB'[01]+'", "name": "constant.numeric.binary.tpg" },
{ "match": "\\bO'[0-7]+'", "name": "constant.numeric.octal.tpg" },
{ "match": "\\bX'[0-9A-Fa-f]+'", "name": "constant.numeric.hex.tpg" }
]
},
"keyword": {
"patterns": [
{ "match": "\\b(?:IF|THEN|ELSE|END)\\b", "name": "keyword.control.tpg" },
{ "match": "\\b(?:FLOAT|CSTRING|BSTRING|FILE)\\b", "name": "storage.type.tpg" }
]
},
"operator": {
"patterns": [
{ "match": "!!", "name": "keyword.operator.concat.tpg" },
{ "match": "\\.(?:AND|OR|NOT)\\.", "name": "keyword.operator.logical.tpg" },
{ "match": "(?:<=|>=|<>|!=|=|<|>|\\+|\\-|\\*|/)", "name": "keyword.operator.tpg" }
]
},
"label": {
"match": "^\\s*[A-Za-z_][A-Za-z0-9_]*:\\s*(?=\\S)",
"name": "entity.name.label.tpg"
},
"identifier": {
"match": "\\b[A-Za-z_][A-Za-z0-9_]{0,23}\\b",
"name": "variable.other.tpg"
}
}
}
4.3 正規表現設計で押さえておくべきポイント
- 余白の取り扱いを明確にする — 行頭が揃わないコメントなどは
^\\s*
を用いて吸収します。 - 終端トークンの誤一致を避ける — 文字列リテラルの閉じクォートは、エスケープ (
\\'
) を許容した上で正しく検出しましょう。 - 単語境界だけに頼らない —
.AND.
のようにピリオドに挟まれた演算子は\\.
を含めたカスタム境界で拾う必要があります。
補足: 色が反映されないときは VSCode コマンド Developer: Inspect Editor Tokens and Scopes で実際に付与されたスコープ名を確認すると原因究明が容易です。
5. テストとデバッグ ― Extension Development Host を活用する
拡張フォルダを VSCode で開き、F5
キーを押すと Extension Development Host(開発用の別インスタンス)が起動します。そこで .tpg
ファイルを作成し、色分けが期待どおりか確認しましょう。tmLanguage.json
を保存すると即座に反映されるため、ホットリロード感覚で調整できます。
テスト用コード例:
; Sample TPG
IF SIGNAL_A > 5 THEN
SET VOLTAGE "CH1" 3.3
ELSE
LOG "Voltage too low"
END
よくあるつまずきと対処法(3 例)
scopeName
の不一致 —package.json
とtmLanguage.json
のscopeName
が揃っているか確認してください。- 正規表現エラー — 無効な正規表現があると該当パターンが無視されます。特にバックスラッシュの JSON エスケープ漏れにご注意を。
- 拡張子の登録漏れ —
.tpg
がcontributes.languages.extensions
に含まれていない場合、VSCode が言語を認識できません。
6. 仕上げ ― package.json
と README.md
を整える
6.1 package.json
の必須フィールド
"contributes": {
"languages": [
{ "id": "tpg", "aliases": ["TPG"], "extensions": [".tpg"] }
],
"grammars": [
{
"language": "tpg",
"scopeName": "source.tpg",
"path": "./syntaxes/tpg.tmLanguage.json"
}
]
}
languages
— 言語 ID・表示名・対応拡張子を宣言します。grammars
— 文法ファイルへのパスとscopeName
を紐付けます。
その他のフィールド(バージョンや出版社名など)はテンプレートのままでも動作しますが、Marketplace に公開する際は適切に記述することをお勧めいたします。
6.2 README.md
に最低限含めたい 3 項目
- 導入手順 — VSIX ファイルのインストールコマンドや Marketplace URL。
- 言語仕様ドキュメントへの導線 — 公式資料や社内 Wiki へのリンク。
- ライセンス — MIT ライセンスなら一行で明記できます。
7. まとめ ― まずは“一色分類”から始めよう
自作シンタックスハイライトの核心は「正規表現を書いて即確認する」の繰り返しです。今回ご紹介した最小実装でも、無彩色だった DSL が一気に読みやすくなることを体感いただけるでしょう。
将来的には以下のような機能拡張も視野に入ります。
- シンボル一覧やジャンプ機能を提供する Document Symbol Provider
- スニペットやコード補完を追加する Completion Provider
- Marketplace への公開と自動更新対応
まずはご自身が日常的に触れている DSL を対象に、10 行の正規表現から始めてみてはいかがでしょうか。きっと開発効率が一段上がるはずです。
参考資料
- Visual Studio Code Extension API(公式)
- TextMate Grammar Documentation
- generator-code GitHub リポジトリ
- Regular Expressions 101(オンライン正規表現テスター)