Visual Studio Code用のtab→space変換のエクステンションを作る

## はじめに
ついにVisual Studio CodeもExtensionを作成できるようになりました。試しにExtensionを作ってみたので、何回かに分けて記事を書いてみたいと思います（アドベントカレンダーとかないとなかなか書かないのでいい機会です）。  

今回作成したサンプルはソースコードのtab（\t）をスペースに変換するエクステンションです。ソースコードはGithubに載せてあるので興味のある方は見てださい。
[TabSpacer](https://github.com/YuichiNukiyama/TabSpacer)

## 環境
今回はの環境はこちら（TypeScriptがDev版なのはご容赦を）。

+ VS Code v0.10.3
+ Node.js v4.2.2
+ npm v2.14.7
+ TypeScript v1.8.0-dev.20151128
+ Yeoman v1.5.0

## generator-codeでExtensionのひな型を作成
VS Codeのエクステンション作成用にgenerator-codeが用意されています。generator-codeを使用すれば簡単にエクステンションのひな型を作成できます。

```
npm install -g yo generator-code
yo code
New Extension(TypeScript)を選択
```

## Extensionの作成
生成されたひな型は初めからデバックやテストなどができるように複数ファイル生成されます。それらのファイルはそのまま使っていくので、今回はpackage.jsonとsrc/extension.tsの2つをカスタマイズしていきます。

### package.json
package.jsonファイルの中で、Extensionの設定として変更する可能性が高い部を抜粋します。このサンプルではVS Code時にエクステンションを有効化して、**ctrl+shift+t**または、**ctrl+shift+z**でエクステンションを実行する設定になっています。

```json:package.json
"categories": [
    "Other"
  ],
  "activationEvents": [
    "*"
  ]
  "contributes": {
    "commands": [
      {
        "command": "extension.tabSpacer",
        "title": "convert tabs to space",
        "description": "Number of spaces is 4."
      },
      {
        "command": "extension.toggleTabSpace",
        "title": "toggle insertSpaces option",
        "description": "tabsize option sets 4."
      }
    ],
    "keybindings": [
      {
        "command": "extension.tabSpacer",
        "key": "ctrl+shift+t"
      },
      {
        "command": "extension.toggleTabSpace",
        "key": "ctrl+shift+z"
      }
    ]
  }
```

| key名 | 値 | 説明|
|:-----------|:------------|:------------|
| categories | Languages, Snippets, Otherなど | エクステンションの種類。Market Placeに公開する際の分類として使用される。 |
| activationEvents | *, onLanguage, onCommandなど | エクステンションの有効化タイミングの指定。*ならVS Code起動時、onLanguageなら特定言語のファイルを開いたとき、onCommandならコマンドを実行したときにエクステンションが有効化する。 |
| commands | 任意のコマンド名 | エクステンションを実行するコマンド。commandはextension.tsファイル内のコマンド名と一致している必要がある。 |
| keybindings | command, keyなど | コマンドを実行するショートカットキーの指定。commandはcommandsで指定したコマンド名と一致している必要がある。keyは+記号でつなげて表記。 |

### extension.ts
extension.tsにはエクステンションのエントリポイントとなる処理を記述していきます。

```ts:extension.ts
import {window, commands, Disposable, ExtensionContext} from 'vscode';
import TabConverter from './tabConverter';

export function activate(context: ExtensionContext) {
    var disposable = commands.registerCommand('extension.tabSpacer', () => {
        // create TabConverter
        let tabConverter = new TabConverter();
        tabConverter.convertTabToSpace();
        window.showInformationMessage('All tabs had been converted to spaces!');
    });
}
```

エクステンション用のAPIは**vscode**モジュールをインポートすることで使えるようになります。ひな型では一部の部品しかインポートされていないので、必要に応じて追記していってください。つづいて、エクステンションの実行は、*commands.registerCommand*のコールバック関数として記述します。第1引数のコマンド名は、先述した*package.json*内の*commands*の値と一致している必要があります。

```ts:TabConverter.ts
import {window, Selection, Position} from 'vscode';

export default class TabConverter {
    public convertTabToSpace() {
        let editor = window.activeTextEditor,
            document = editor.document,
            startPos = new Position(0, 0),
            endPos = new Position(document.lineCount - 1, 10000),
            selection = new Selection(startPos, endPos),
            text = document.getText(),
            newText = '';

        editor.edit(edit => {
            // create spaces as same as number of tabSize
            let replaceValue = '';
            for (let i = 0; i  < options.tabSize ; i++) {
                replaceValue += " ";
            }

            // replace all tabs to space
            newText = text.replace(/\t/g, replaceValue);
            edit.replace(selection, newText);
            editor.selections = [selection];
        }).then(bool=> {
            if (bool) {
                console.log('executed sucessfully.');
            } else {
                console.log('failed.');
            }
        });
    }
}
```

VS Codeで開かれているテキストエディタにアクセスするためには、*editor.activeEditor*を使います。また、エディタの*getText*でテキスト内のコードを取得できます。
今回は取得したコード内のタブをスペースに変換してエディタに反映します。まず、エディタを変更するには、*edit*メソッドを実行してコールバック関数を登録します。コールバク関数には、*textEditorEdit*型の引数が渡るので、*replce*メソッドを呼び出してタブをスペースに変換します。ただ、変換しただけではエディタに反映されないため、*editor.sections*に変更した値を代入します（Sectionは行番号と文字位置でエディタのセクションを表すプロパティです）。これで晴れてタブをスペースに変換できました。

## 終わりに
VS Codeはエクステンションのために用意されているAPIが少ないです。。。また、まだ情報が少ないので苦戦するかもしれませんが、ぜひ作成してみてください。余裕があればMarket Placeへの登録なども試してみます。

## 参考
[Extending Visual Studio Code](https://code.visualstudio.com/docs/extensions/overview)
[Visual Studio Codeのエクステンションを作成する](http://www.atmarkit.co.jp/ait/articles/1511/30/news030.html)
[vscode-MDTools](https://github.com/Microsoft/vscode-MDTools)
[TabSpacer](https://github.com/YuichiNukiyama/TabSpacer)