• Windows Terminal ベスト設定 第9回「カスタムコマンドを作る」

Windows Terminalの設定ファイルsettings.jsonを使うと、キー割り当てだけでなく、コマンドパレットから実行可能なコマンドを定義することができる。これをカスタムコマンドという。この機能を使うと、さまざまな処理を簡単に実行できるようになる。特に、プロファイルやカラースキーマの選択を伴うようなコマンドは、プロファイル定義などに依存せず簡単にコマンドを作成できる仕組みがある。

今回は、settings.jsonによるカスタムコマンド定義の方法を解説する。なお、検証は、Windows Terminal Ver.1.18.1462(プレビュー版)、同1.17.11461.0(安定版)で行った。

JSONに関して

コマンドの定義は、JSONファイルの上で行うため、JSONの用語(表01)と、Windows Terminal(表02)の用語が混在するため、用語を整理しておいた。

  • ■表01

  • ■表02

簡単にいうと、JSONは、文字列、数値と特殊トークン(true、false、null)の単純な「値」(一般用語と区別するため以下JSON値と表記する)と、オブジェクトと配列という構造を持つJSON値から構成されている。オブジェクトは、名前/値ペアから構成され、名前/値ペアは、名前(キー)とJSON値をコロン“:”でつなげたもの。名前はJSONの文字列を使い、名前/値ペアが複数ある場合には、カンマで区切る。

これに対して配列は、JSON値をカンマで区切って並べたもの。オブジェクトも配列もJSON値なので、名前/値ペアの値としても、配列の要素にすることもできる。

Windows Terminalのsettings.jsonは、JSONで記述されており、“//”で始まるMicrosoftの拡張である注釈(JSONC)を入れることができる。

JSONファイルを編集する場合、注意するのは、カンマの扱いである。JSONでは、オブジェクト内の名前/値ペアと配列の要素をカンマで区切るが、最後の名前/値ペア、要素の後ろにカンマを付けるとエラーになる。このため、オブジェクトや配列の中に要素などを挿入する場合、カンマに注意する必要がある。

配列やオブジェクトの最後に要素や名前/値ペアを挿入する場合には、末尾(閉じ波括弧や閉じ角カッコの直前)には、カンマを付けない。

逆に、後続する要素や名前/値ペアがあるときには、必ずカンマを付ける必要がある。

また、項目を削除する場合も、最後の項目なら、直前の項目の末尾のカンマを削除する必要がある。

このことを考えると、項目の挿入は、できれば、配列やオブジェクトの先頭(開き波括弧や開き角括弧の直後)で行い、末尾にカンマを付けるようにしたほうがよい。

settings.jsonについて

Windows Terminalのsettings.jsonに関しては過去にも説明したが、その後のバージョンアップなどでプロパティ名などが変化している。このため、ここで再び解説を行う。

(図01)は、settings.json全体を示すもの。settings.jsonは全体が1つのオブジェクトになっており、その中に、単純なの名前/値ペアと、配列、オブジェクトにより定義される設定値がある。

  • 図01: settings.jsonは全体が1つのオブジェクトになっており、その中に全体の設定を行うプロパティと、構造を持つ7つのプロパティがある

構造を持つ設定値には、(表03)のようなものがある。この記事では、JSONの形式から「アクション配列」、「プロファイルオブジェクト」などと表記する。また、JSONオブジェクトの名前/値ペアを「プロパティ」と表記する。

  • ■表03

コマンドの定義は、アクション配列で行う。アクション配列は、settings.jsonの中で“actions”プロパティの値となる配列である。その要素になるオブジェクトを「コマンド」と呼ぶ。

settings.jsonのアクション配列に関する用語は少し混乱している。というのは、初期のWindows Terminalでは、この部分は、キー割り当て用のkeybindings配列だった。このとき、キーと組み込みの機能の対応は"command"プロパティを持つオブジェクト使って記述していた。このため、配列の要素は「コマンド」と呼ばれていた。その後、組み込みコマンドには引数を持つものが登場した。このとき、現在の引数プロパティを持つ“action”が導入された。さらに、コマンドパレットが導入されると、カスタムコマンドが追加され、全体のプロパティ名が“actions”に変更になった。

コマンドを定義する

settings.jsonのアクション配列内に定義されたコマンドは、すべてコマンドパレットから実行できる。このため、コマンドは必ずしもキー割り当てを行う必要がない。コマンドのうち、“keys”プロパティを含むものを「キー割り当て」と呼ぶ。

(リスト01)は、単純なコマンドの例である。これにkeysプロパティを追加すれば、キー割り当てになる。しかし、keysプロパティがなくても、このコマンドは、コマンドパレットに表示される。コマンドパレットでは、コマンドは、“name”や“icon”プロパティで指定された名前やアイコンで表示される。これらがない場合、組み込みコマンドの表示名がコマンド名となる。

■リスト01
※actionsブロック内に記述


{
    "name": "0 new-tab PS7",
    "icon" : "\uE78B",
    "keys" : "ctrl+alt+shift+x",
    "command" : {"action" : "newTab","profile":"PowerShell7" }
}

コマンドパレットでは、コマンド実行前にユーザーにサブコマンド(選択肢)を提示し、選択されたサブコマンドに対応するパラメーターでコマンドを実行する機能がある。サブコマンドを定義した例が(リスト02)だ。ここでは、commands配列(複数形であることに注意)を使って、複数のコマンドを選択できるようにしてある。つまり、commands配列を使って、複数のコマンドを定義すると、コマンドパレットでは、これらをサブコマンドとして表示する。

■リスト02


{
    "name": "Simple Choice command sample 'select copy'",
    "commands": [
        {
            "name": "copy all",
            "keys": "ctrl+alt+shift+c",
            "command": {"action": "copy", "copyFormatting": "all","singleLine": false }
        },
        {
            "name": "none/sigleline",
            "keys": "ctrl+alt+shift+n",
            "command": {"action": "copy","copyFormatting": "none","singleLine": true }
        },
        {
            "name": "HTML Copy",
            "keys": "ctrl+alt+shift+h",
            "command": {"action": "copy","copyFormatting": "html","singleLine": false}
        },
        {
            "name": "RTF Copy",
            "keys": "ctrl+alt+shift+r",
            "command": {"action": "copy","copyFormatting": "rtf","singleLine": false}
        }
    ]
}

(リスト03)も選択肢を持つコマンドだ。この場合、"iterateOn"プロパティで"profiles"を指定しているため、コマンドを選択(写真01)すると、プロファイルの選択画面(写真02)が表示され、選択したプロパティを使って、"command"で定義されたコマンドが実行される。“iterateOn”プロパティを使うことで、リスト02のように複数のコマンドを記述することなく、プロファイルの数だけコマンドが自動的に作られる。これを応用することで、プロファイルを引数に持つ、さまざまなコマンドを定義できる。

■リスト03


{
    //コマンド名の定義。コマンドパレットに表示されるコマンド名
    "name": "New Window...",
    //iterationOnで複数コマンドを定義するからcommandsが必要
    "commands": [
        {
            // commandsの中は「オブジェクト」の配列
            // profileで複数のコマンドを生成
            "iterateOn": "profiles",
            "icon": "${profile.icon}",
            "name": "${profile.name}",
            //コマンドの雛形を定義
            "command": { "action": "newWindow", "profile": "${profile.name}" }
            }
        ]
}
  • 写真01: コマンドパレットで、カスタムコマンド名に含まれる文字を入れていくとインクリメンタル検索で、コマンドのリストが絞られていく。カスタムコマンド名をアルファベットのみにしておくと選択しやすい

  • 写真02: commandsプロパティの内側で「"iterateOn": "profiles"」を使うと、プロファイルの選択が行え、選択したプロファイルでコマンドが実行される

(リスト04)は、さらに複雑な例で、選択が2段階になる。コマンド選択後、最初の選択肢は、前と同じくプロファイルの選択だが、2つめ(写真03)は、一番内側の“commands”配列で定義したコマンドの選択である。ここでは、"splitPane"のパラメーターである"split"のプロパティ値を自動(auto)、水平(horizontal)、垂直(vertical)と指定した3つのコマンドを定義してある。

■リスト04


{
    "name": "2 Split Pane H/V/Auto ..."
    //利用頻度の高いコマンドは先頭に数字をつけるとアクセスしやすい
    "commands": [
        {
            "iterateOn": "profiles",
            "name": "${profile.name}..."
            "icon": "${profile.icon}",
            "commands": [
                //繰り返しを使って、さらに3つのコマンドを定義
                //プロファイルの選択ののち、さらに3つのコマンドの選択肢が出る
                {"command": {"action": "splitPane", "profile": "${profile.name}", "split": "auto"      } },
                {"command": {"action": "splitPane", "profile": "${profile.name}", "split": "horizontal"} },
                {"command": {"action": "splitPane", "profile": "${profile.name}", "split": "vertical"  } }
            ],
        }
    ],
},
  • 写真03: commandsプロパティを入れ子にすると、プロファイル選択のあと、さらに選択肢(ここでは"splitPane"の"split"パラメータ)を表示する

コマンドに使うプロパティ

コマンドは、JSONのオブジェクトで定義する。これには、単純コマンドと複雑コマンドの2つに分類できる。単純コマンドは、"command"プロパティが1つのみのコマンドだ。キー割り当てで使われるのは基本的に単純コマンドだ。

単純コマンドは、commandプロパティでWindows Terminalの組み込み「機能名」を指定するか、actionオブジェクトを指定する。


{"command":<機能名>}
{"command":<actionオブジェクト>}

<actionオブジェクト>は、機能名と引数プロパティを使い、以下のように定義する。


{"action":<機能名>,<引数プロパティ>,……}

引数プロパティは、機能名ごとに定義がある。機能名や引数プロパティに関しては、Microsoftのサイト「Windows ターミナルのアクション」などを参照していただきたい。

これに対して、複雑コマンドは、内部に"commands"プロパティを持ち、複数のコマンドを内部に持つことができる。このコマンドは単純コマンドでも、複雑コマンドでもよい。


{ ... "commands": [ {<コマンド>}, ... ]... }

複雑コマンドは、コマンドパレットで選択されると、サブコマンドの選択を表示する。サブコマンドの選択がすべて終了すると、選択されたパラメーターを使ってコマンドが実行される。

コマンドでは(表04)にあるようなプロパティを指定できる。表のうち、nameとicomはどこでも指定が可能で、コマンドパレットに表示される。もし、nameがなければ、指定されたcommand名と引数のプロパティ値が表示に使われる。日本語の場合、翻訳された名前が表示されるが、一部の引数プロパティ値には、日本語翻訳がない場合がある。

  • ■表04

コマンドパレットでは、選択肢が表示されるコマンドにはコマンド名の末尾に“...”を付けている。これにより、コマンドに選択肢があることを示しているので、選択肢のあるコマンドを定義する場合には、できれば、名前(nameプロパティ)は、このやりかたに従ったほうがよい。

"commands"プロパティの値は配列になるため、複数のコマンドを持つことができる。このとき、"iterateOn"を使うことで、プロファイルやカラースキーマを引数プロパティとして持つコマンドを自動的に作成することができる。

現在のWindows Terminalでは、プロファイルまたはカラースキーマのどちらか1だけが"iterateOn"プロパティの値として設定できる。

"iterateOn"を使ったとき、プロパティ値として「${profile.name}」と「${profile.icon}」、「${scheme.name}」の3つが文字列中で利用できるようになる。それぞれ、選択後に、プロファイルのnameプロパティ値、iconプロパティ値、カラースキーマのnameプロパティ値に置換される。

カスタムコマンドでは、“keys”プロパティでキー割り当てができるが、これはサブコマンドを含まないものだけだ。commands配列内のコマンドもキー割り当てができない。こうした場合、コマンドパレットや設定ページの「操作」でも、このキー定義は表示されない。しかし、これらは、settings.jsonの読み込みではエラーにならないため注意が必要だ。

現在のところ"iterateOn"では、プロファイル("profiles")またはカラースキーマ("schemas")が指定でき、名前/値ペアの値(プロパティ値)では、 "${profile.name}"のようにプロファイルオブジェクトのnameプロパティを参照できる。"iterateOn"を含むオブジェクトは、複数のコマンドになるため、全体を"commands"で囲む必要がある。

カスタムコマンドの作成を使うことで、自分で必要な設定をまとめて行うことが可能になる。コマンドラインでの作業が多いようなら、作業効率化のために考えてみるといいだろう。カスタムコマンドは、ユーザーが自由に名前を付けることができるため、日本語版でコマンド名が覚えにくい場合には、欧文の名前を持つコマンドを定義することができ、1種のエイリアス(別名)としても利用できる。コマンド名を欧文とすると、コマンドパレットでのインクリメンタル検索が便利になる。

>> Windows Terminal ベスト設定 連載バックナンバー
https://news.mynavi.jp/tag/winterminal/