VSCodeのテーマ色を好きなとこだけ改造する

投稿日 最終更新日

2025年10月19日

過去にもVSCodeのテーマ色を初音ミク色に変える方法をブログに残してるんだけど、あれから進化しました。

簡単に方法を記すと

  1. 好きなベーステーマを入れる
  2. 開発者: エディター トークンとスコープの検査を使って変えたい部分のトークン名を調査する
  3. settings.jsonへ記述する

といった感じ。

好きなベーステーマを入れる

全てを1から弄るのは面倒なので、ある程度理想のテーマを入れる。

わたしの場合はマーケットプレイスにあるJetbrains Dark Themeというのを利用した。これ、優しい色で変に尖ってないので好き。というかJetbrainに慣れすぎて親しみがすごい。

テーマは以下のどちらかで変更可能

  • ファイル→ユーザー設定→テーマ→配色テーマ
  • ショートカット: Ctrl+K Ctrl+T

「開発者: エディター トークンとスコープの検査」を使って変えたい部分のトークン名を調査する

ここらへんはVSCode公式ドキュメントのsyntax highlight guideが良さげ。

###「TextMate」と「Semantic」
実際にトークンを取得する前にこのテキストメイトとセマンティックについて理解しておきたい。

VSCodeでは適当な言語で書かれたテキストを構文的に分類し色分けするために、TextMateとSemanticという2つの方法を採っている。

簡単に言えば以下のように言える

  • TextMate
    • 正規表現で解析しトークン化する
    • どんな言語でも解析できる(精度の良し悪しはある)
    • 正規表現なので解析が早い
  • Semantic
    • 文法的に解析しトークン化する
    • 言語サーバーが存在する言語しか解析できない
      • TypescriptはVSCodeがネイティブに対応しているっぽいけど、PHPとかはネイティブに対応していない。そのため、サードパーティの言語サーバーを使う必要あり。
    • TextMateより精度が高く、TextMateと色の設定が被ってもSemanticの方が優先度が高い

基本的にはTextMateのトークンを使い、Semanticが存在したらそっちも使う方針になるのかな。

「エディター トークンとスコープの検査」をショートカットに登録する

Ctrl+Shift+Pでコマンドパレットを出して、tokenと入力すると「開発者: エディター トークンとスコープの検査」というのが出てくる。

これを頻繁に使うので、歯車マークからショートカットキーで呼び出せるよう適当なキーバインドを設定しておく。
わたしの場合はCtrl+Shift+C Ctrl+Shift+Oにした。Colorの頭文字2文字を取ってきている。

トークンを調査する

この色変えたい! っていう部分があるファイルを開き、設定したショートカットキーでエディター トークンとスコープの検査を有効にする。

すると、以下のようになんか色々情報が出てくる

textmate scopesの欄にある複数のトークンが、TextMateのトークン。
このvalidatedには

  1. variable.other.php
  2. meta.class.body.php
  3. meta.class.php
  4. source.php
  5. meta.embedded.block.php
  6. text.html.php

のように6つのトークンが当てはまっていて、上にあるトークンほど具体的な正規表現に当てはまっている。
下にあるトークンを指定して色設定すると、他の部分にも影響が出る可能性があるので、できる限り上にあるトークンを使って影響範囲を狭めたほうが良いのかなと思う。

名前的にもvariable.other.phpというのはPHPの変数に間違いなさそうなので、今回はこのトークンを使う。

他にも、TypeScriptとかのトークンを見てみると

のようにtextmate scopesの他にsemantic token typeという欄がある場合がある。
これがセマンティックトークンで、このusePagefunctionという分析がされたみたい。

「settings.json」へ記述する

トークン名を取得できたら、settings.jsonへ色の設定を記述する。

setting.jsonCtrl+Pでコマンドパレット開いてsettings jsonとかで調べると4択くらい出てくるので、好きな影響範囲のものを開けばおkだと思う。
わたしはユーザー設定に記述する。

TextMateトークンを使って色指定をしたい場合、以下のようになる

    // TextMateで色設定
    "editor.tokenColorCustomizations": {
        "[Default Dark Modern]": {
            "textMateRules": [
                {
                    "scope": "variable.other.php",
                    "settings": {
                        "foreground": "#ff6467" // テト色
                    }
                }
            ]
        }
    },

"[Default Dark Modern]": {の部分は自身の使ってるテーマの名前を入れるか、指定せずブロックごと消してもおk。ただ、指定したほうが他のテーマにしたときに色構成が変なことになることを防げる。

これで設定を保存し確認してみると

のようにしっかり変数部分の色が変わっているのがわかる。

Semanticで指定する場合は

	// Semanticで色指定
    "editor.semanticTokenColorCustomizations": {
        "[Default Dark Modern]": {
            "rules": {
                "function": "#ff6467", // テト色
            }
        }
    },

のように指定する。

設定を保存し確認すると

のように設定が反映されており、TextMateを無効化しているのもわかる。

わたしのjson

最終的に以下のような見た目に
php

tsx

結果魔改造となりました。

    "workbench.colorCustomizations": {
        "[Jetbrains Dark Theme]": {
	        // 基本色の設定
            "editor.foreground": "#e1dad2eb"
        }
    },
    // TextMateで色設定
    "editor.tokenColorCustomizations": {
        "[Jetbrains Dark Theme]": {
            "textMateRules": [
                {
                    "scope": [
                        "entity.name.function",
                        "support.function",
                        "support.function.misc.css",
                        "support.function.construct.php",
                        "markup.heading.markdown",
                    ],
                    "settings": {
                        "foreground": "#26BDA4"
                    }
                },
                {
                    "scope": [
                        "keyword",
                        "keyword.operator",
                        "keyword.operator.assignment.tsx",
                        "storage.type",
                        "storage.modifier",
                        "storage.type.function",
                        "support.type.primitive",
                        "support.type.builtin",
                        "entity.name.tag",
                        "constant.character.escape",
                        "constant.language",
                        "punctuation.separator.comma",
                        "punctuation.terminator.statement",
                        "punctuation.terminator.expression.php",
                        "punctuation.terminator.rule.css",
                        "punctuation.separator.array.json.comments",
                        "punctuation.separator.dictionary.pair.json.comments",
                        "punctuation.separator.dictionary.key-value.json.comments",
                        "punctuation.definition.list.begin.markdown",
                    ],
                    "settings": {
                        "foreground": "#ee3d66eb"
                    }
                },
                {
                    "scope": [
                        "string",
                        "string.regexp",
                        "string.unquoted.plain.out.yaml",
                        "meta.embedded.expression string.quoted",
                        "punctuation.support.type.property-name.end.json.comments",
                        "punctuation.support.type.property-name.begin.json.comments",
                        "markup.inline.raw.string.markdown",
                    ],
                    "settings": {
                        "foreground": "#e2d86deb"
                    }
                },
                {
                    "scope": [
                        "punctuation.definition.tag",
                    ],
                    "settings": {
                        "foreground": "#f89f19"
                    }
                },
                {
                    "scope": [
                        "entity.name.type.interface",
                        "entity.name.type.class",
                        "entity.other.attribute-name",
                        "punctuation.section.embedded.begin.php",
                        "variable.other",
                        "variable.object.property",
                        "variable.other.property",
                        "variable.other.readwrite",
                        "variable.other.constant",
                        "variable.other.object",
                        "variable.language.this",
                        "variable.parameter",
                        "variable.parameter.ts",
                        "variable.other.object.property",
                        "keyword.control.as.tsx",
                        "support",
                    ],
                    "settings": {
                        "foreground": "#e1dad2eb"
                    }
                },
                {
                    "scope": [
                        "comment.block.documentation",
                    ],
                    "settings": {
                        "foreground": "#808080"
                    }
                },
                {
                    "scope": [
                        "entity.name.type.tsx",
                        "entity.name.type.ts",
                        "entity.name.type.interface.ts",
                        "meta.type.parameters.tsx entity.name.type.tsx",
                        "keyword.control.type.tsx",
                    ],
                    "settings": {
                        "foreground": "#6190f4eb"
                    }
                },
                {
                    "scope": [
                        "constant.numeric",
                        "entity.name.tag.yaml",
                        "string.regexp punctuation.definition.string.begin",
                        "string.regexp punctuation.definition.string.end",
                        "support.type.property-name.json.comments",
                        "support.constant.property-value.css",
                    ],
                    "settings": {
                        "foreground": "#9876AA"
                    }
                },
            ]
        }
    },

以上です。