【VSCode】複数のコマンドを一つのキーで実行したい

この記事は、[ニフティグループ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nifty) 8日目の記事です。

昨日は @iNakamura さんの「[自然言語処理でツイートがバズるかをちゃんと予測する](https://qiita.com/iNakamura/items/b622ec3034d0d519d919)」でした。
私も研究で二値分類してましたが、特にデータ集め(ラベル付け)に苦労しましたね…。
来年のリベンジに期待しています！

# はじめに
VSCodeでコーディングなどをする際に、同じような作業、コマンドを打つ場面に度々遭遇すると思います。このとき、一つのキーで複数のコマンドを順番に実行できたらなぁと思い、そのような拡張機能を探したところ、[multi-command](https://marketplace.visualstudio.com/items?itemName=ryuta46.multi-command)というVSCodeの拡張機能を見つけたので、今回はそちらについて紹介したいと思います。

なお、本記事はWindows10を前提としています。

# multi-command について
[multi-command](https://marketplace.visualstudio.com/items?itemName=ryuta46.multi-command)とは、複数のコマンドを一つのコマンドとして定義する(まとめる)ことができる拡張機能です。定義したコマンドにキーを割り当てることで、一つのキーで複数のコマンドを実行することができます。

まず、この拡張機能の紹介の前にVSCodeのコマンドとキーバインドについて説明していきます。

## コマンド
VSCodeのコマンド([Command](https://code.visualstudio.com/api/extension-guides/command))とはファイルの保存やコメントアウトの切り替えなどのアクションを実行するトリガーです。
これらのコマンドにはコマンドIDが割り振られています(例：「ファイル: 保存」には`workbench.action.files.save`)。
利用できるコマンドはキーボードショートカット(Ctrl+K Ctrl+S)から確認することができます。

### コマンドIDの確認方法
コマンドIDは後述するキーバインドで利用するため、ここでコマンドIDの確認方法について説明します。
コマンドIDの確認手順は以下のとおりです。

1. キーボードショートカット(Ctrl+K Ctrl+S)を開く。
2. コマンドIDを確認したいコマンドを右クリックする。
3. 「コマンドIDのコピー」を選択するとコマンドIDがクリップボードにコピーされる

また、コマンドパレット(Ctrl+Shift+P)を開き、「`workbench.action.openDefaultKeybindingsFile`」と入力することで開かれるJSONファイルから、利用可能なコマンドIDの一覧を確認することもできます。

## キーバインド
コマンドはキーバインド([KeyBinding](https://code.visualstudio.com/docs/getstarted/keybindings))によって、キーを割り当てることができます。
コマンドパレットを開き、「`workbench.action.openGlobalKeybindingsFile`」と入力することで、キーバインドを設定できるJSONファイル(`keybindings.json`)が開かれます。
このキーバインドファイルは以下のような形式で記述します。

```json:keybindings.json
[
    {
        "key": "<コマンドを実行するショートカットキー>",
        "command": "<実行されるコマンドID>",
        "when": "<[オプション] このキーバインドが有効になる条件>"
    },
    ・・・
]
```

`key`に指定できるキーの一覧：[Key Bindings#Accepted keys](https://code.visualstudio.com/docs/getstarted/keybindings#_accepted-keys)
`when`に指定できる条件の一覧：[Key Bindings#'when' clause contexts](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts)

### 引数`args`について
また、いくつかのコマンドには引数(`args`)を指定することもできたりします。
`args`を指定できるコマンドとして「[type](https://code.visualstudio.com/docs/getstarted/keybindings#_command-arguments)」, 「[workbench.action.terminal.sendSequence](https://code.visualstudio.com/docs/editor/integrated-terminal#_send-text-from-a-keybinding)」, 「extension.multiCommand.execute(後ほど紹介)」などが挙げられます。
以下は、エディタ上のテキストにフォーカスしているときに、F1キーを押すと「Hello, world!」と入力されるキーバインドの例です。

```json:keybindings.json
[
    {
        "key": "F1",
        "command": "type",
        "args": {
            "text": "Hello, world!"
        },
        "when": "editorTextFocus"
    }
]
```

このファイルの詳しい仕様については、[Key Bindings#Keyboard rules](https://code.visualstudio.com/docs/getstarted/keybindings#_keyboard-rules)を参照してください。

## multi-commandの設定方法
コマンドとキーバインドについて説明したところで本題に戻ります。
multi-commandを用いて複数のコマンドを一つのキーで実行する手順は以下のとおりです。

1. 設定ファイルで複数のコマンドを一つのコマンドとして定義する。
2. 定義したコマンドにキーを割り当てる。

それでは、これらの手順について説明していきます。

### (1) 設定ファイルで複数のコマンドを一つのコマンドとして定義する。
まず、設定を記載するJSONファイル(`settings.json`)を開きます。
コマンドパレットを開き、「`workbench.action.openSettingsJson`」と入力することでこのファイルを開くことができます。

この開いたファイルに対して、以下のような記述を追加することで複数のコマンドを一つのコマンドとして定義することができます。
以下の例では、「コマンドA → コマンドB → コマンドC」の順番に実行するコマンドを<コマンドID>として定義しています。
また、引数`args`を指定するコマンドを実行したい場合、コマンドCのように記述します。

```json:settings.json
{
    ・・・
    "multiCommand.commands": [
        {
            "command": "<コマンドID>",
            "sequence": [
                "<コマンドAのコマンドID>",
                "<コマンドBのコマンドID>",
                {
                    "command": "<コマンドCのコマンドID>",
                    "args": {
                        <コマンドCの引数>
                    }
                }
            ]
        },
        ・・・
    ],
    ・・・
}
```

### (2) 定義したコマンドにキーを割り当てる。
次に、手順(1)で定義したコマンドにキーを割り当てます。
`keybindings.json`を開き、以下のように記述します。

```json:keybindings.json
[
    ・・・
    {
        "key": "<コマンドを実行するショートカットキー>",
        "command": "extension.multiCommand.execute",
        "args": {
            "command": "<手順(1)で定義したコマンドID>"
        }
    },
    ・・・
]
```

## 記入例
### 例1. 行末に`;`を追加して改行する
以下の複数のコマンドを`multiCommand.addSemicolonAndLine`という一つのコマンドとして定義しています。

1. 行末に移動する(`cursorEnd`)
2. 「`;\n`」と入力する(`type`)

```json:settings.json
{
    "command": "multiCommand.addSemicolonAndLine",
    "sequence": [
        "cursorEnd",
        {
            "command": "type",
            "args": {
                "text": ";\n"
            }
        }
    ]
}
```

`multiCommand.addSemicolonAndLine`にF2を割り当てます。
こうすることで、エディタ上のテキストにフォーカスしているときにF2を押すと、行末に`;\n`が入力されます。
呼び出したい定義したコマンドIDは`"args"`内の`"command"`に指定するところに注意。

```json:keybindings.json
{
    "key": "F2",
    "command": "extension.multiCommand.execute",
    "args": {
        "command": "multiCommand.addSemicolonAndLine"
    },
    "when": "editorTextFocus"
}
```

### 例2. 現在開いているPythonファイルを実行する[^1]
以下の複数のコマンドを`multiCommand.executePython`という一つのコマンドとして定義しています。

1. ファイルを保存する(`workbench.action.files.save`)
2. ターミナルにフォーカスする(`workbench.action.terminal.focus`)
3. 「`python '${file}'\n`」をターミナルに送信(実行)する(`workbench.action.terminal.sendSequence`)<br>
`${file}`は現在開いているファイルのパスに置き換わります。<br>
利用できる変数の一覧は[Variables Reference](https://code.visualstudio.com/docs/editor/variables-reference)を参照してください。


```json:settings.json
{
    "command": "multiCommand.executePython",
    "sequence": [
        "workbench.action.files.save",
        "workbench.action.terminal.focus",
        {
            "command": "workbench.action.terminal.sendSequence",
            "args": {
                "text": "python '${file}'\n"
            }
        }
    ]
}
```

`multiCommand.executePython`にF3を割り当てます。
エディタ上のテキストにフォーカスしているときにF3を押すと、現在開いているPythonファイルを実行します。

```json:keybindings.json
{
    "key": "F3",
    "command": "extension.multiCommand.execute",
    "args": {
        "command": "multiCommand.executePython"
    },
    "when": "editorTextFocus"
}
```

# おわりに
拡張機能multi-commandによって、複数のコマンドを一つのキーで実行できるようになりました。
日常的に行っている作業に対して積極的に使っていきたいですね。

明日の担当は@nakanowaiさんです。
よろしくおねがいします！

# 参考サイト
- [Commands \| Visual Studio Code Extension API](https://code.visualstudio.com/api/extension-guides/command)
- [Visual Studio Code Key Bindings](https://code.visualstudio.com/docs/getstarted/keybindings)
- [Visual Studio Code Variables Reference](https://code.visualstudio.com/docs/editor/variables-reference)
- [Integrated Terminal in Visual Studio Code](https://code.visualstudio.com/docs/editor/integrated-terminal)
- [multi\-command \- Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ryuta46.multi-command)
- [VSCode の拡張 multi\-command を公開しました](https://ryuta46.com/173)
- [自作 VSCode 拡張 multi\-command がわりと使えそう](https://ryuta46.com/189)

[^1]: ターミナル上でPythonファイルを実行するだけなら`python.execInTerminal`だけで事足ります。