Visual Studio Code はじめての拡張機能開発

# Visual Studio Code はじめての拡張機能開発

最近、Visual Studio Codeを使い始めました。  
主に、メモ程度の文書作成ですが、Markdpwn形式で作成して、まとめています。Previewが簡単に見れるのがいいですね。  
さて、本題ですが、ほしい機能が見つからなく、拡張機能を作ることができるので、作ってみたいと思います。といっても、作り方がわからないので、HelloWorldです。  

## Hollo worldを表示する拡張機能

ここに書くサンプルは、、公式サイトに載っています。  
[公式サイト Example - Hello World](https://code.visualstudio.com/docs/extensions/example-hello-world)

## 開発環境

- Windows 10 Pro 1607
- Visual Studio Code v1.4
- node.js v6.3.1

## 環境構築
### Node.js
まずは、Node.jsが必要なので、インストールをします。
Node.jsには、LSTとCurrentがありますが、そろそろ、LSTになりそうなので、今回は、**Currentバージョン**です。  
Node.jsがインストールされたら、環境変数のPATHを通します。  
デフォルトだと、「*C:\Program Files\nodejs*」に、インストールされます。

>「システムプロパティ」ウィンドウの「詳細設定」タブにある、「環境変数」ボタンで設定できます。  
>一例として、コントロールパネル > システム > システムの詳細設計 から「システムプロパティ」が表示できる。

### コマンドの確認

インストール・設定後に、コマンドプロンプトから、確認をします。

- Node.js

>\>node -v  
>\>v6.3.1  
>\>npm -v  
>\>3.10.3  

- code (Visual Studio Code本体)

念のため、code.exe（Visual Studio Code本体）が実行できるか確認しておきましょう。
コマンドプロンプトから、確認します。

>\>code -v  
>\>1.4.0  

実行できなかった場合は、「*C:\Program Files (x86)\Microsoft VS Code*」（デフォルト）を入れてください。

### ジェネレータのインストール
npmコマンドを使って、拡張機能のジェネレータをインストールします。コマンドプロンプトから実行します。
>\>npm install -g yo generator-code
>>WARNが出ましたが、解消しないで、進めてます。--save付ければ大丈夫？  
>>npm install -g yo generator-code --save

### ひな形の作成
ジェネレータを使って、拡張機能のひな形を作成します。\
以下を、実行すると、*カレントディレクトリ*に、コマンド実行中に指定した名称で、フォルダおよび、必要なファイルが作成されます。

>\>yo code

質問されるので、TypeScript（そのまま、Enter）を選んで、Extention名を「*helloworld*」にします。
あとは、デフォルトのままで、Enterを押します。

|質問|回答|備考|
|:--:|:--:|:--:|
|? What type of extension do you want to create?|New Extention (TypeScript)|拡張機能のタイプ|
|? What's the name of your extension?|helloworld|拡張機能名|
|? What's the identifier of your extension?|helloworld|拡張機能のID。公開しないので、そのまま。|
|? What's the description of your extension?||拡張機能の説明。今のところ、空で大丈夫です。|
|? What's your publisher name? |helloworld|公開者。公開しないので、そのまま。|
|? Initialize a git repository?|Yes|gitの初期化。ローカルリポジトリの作成。|

```
     _-----_     ╭──────────────────────────╮
    |       |    │   Welcome to the Visual  │
    |--(o)--|    │   Studio Code Extension  │
   `---------´   │        generator!        │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? helloworld
? What's the identifier of your extension? helloworld
? What's the description of your extension?
? What's your publisher name? helloworld
? Initialize a git repository? Yes
   create helloworld\.vscode\launch.json
   create helloworld\.vscode\settings.json
   create helloworld\.vscode\tasks.json
   create helloworld\typings\node.d.ts
   create helloworld\typings\vscode-typings.d.ts
   create helloworld\test\extension.test.ts
   create helloworld\test\index.ts
   create helloworld\.vscodeignore
   create helloworld\.gitignore
   create helloworld\README.md
   create helloworld\vsc-extension-quickstart.md
   create helloworld\tsconfig.json
   create helloworld\src\extension.ts
   create helloworld\package.json

・・・・・・・・・・・
いくつかメッセージ
・・・・・・・・・・・

Your extension helloworld has been created!

To start editing with Visual Studio Code, use the following commands:

     cd helloworld
     code .

Open vsc-extension-quickstart.md inside the new extension for further instructions
on how to modify, test and publish your extension.

For more information, also visit http://code.visualstudio.com and follow us @code.

```

## 開発とデバッグ
Visual Studio Code(VS Code)を起動します。
ジェネレータ実行時のメッセージに書かれている、コマンドを実行します。

>\>cd helloworld  
>\>code .

### はじめに
ひな形作成時のメッセージに出力されている通り、「vsc-extention-quickstart.md」を開いて説明を見てみます。  
Markdown形式なので、プレヴュー切り替えでHTMLにして、見ます。「**ctrl + shift + v**」もしくは、右クリックのプレビュー。
記載内容のざっくりですが、説明です。

1. package.jsonとsrc/extension.tsの簡単な説明
1. 実行方法。「F5」、もしくは、「ビューバーのデバッグアイコンを押下」
1. 実行後の変更は、リロード方法。 「ctrl + r」
1. API一式 「node_modules/vscode/vscode.d.ts」に記載
1. テストコードの実行。

> ctrl + shift + dで、デバッグウィンドウを開き、「Launch Tests」を設定する。
> F5を押して、拡張機能がロードされたVS Codeを立ち上げる
> テスト結果は、デバッグコンソールに出力される
> test/extension.test.ts、もしくは、testフォルダ内に新しいテストファイルをいれます
>> ファイル名は、「**.test.ts」のようにします。

主要なファイルの表

| ファイル名 | 説明 |
|:--:|:--:|
|vsc-extension-quickstart.md|クイックスタートマニュアル|
|package.json|プラグインの定義ファイル|
|src/extension.ts|拡張機能のメインファイル。|
|tsconfig.json|TypeScriptのビルドオプションファイル|

### デバッグ
では、実際に動かしてみましょう。

1. VS Codeをアクティブにして、「F5」、もしくは、「ビューバー」の「デバッグ」アイコンを押します。
1. helloworldの拡張機能が有効になった、新しくVS Codeが開かれます。
1. 新しいVS Codeをアクティブにして、コマンドパレットを開きます。「**ctrl + shift + p**」。
1. command指定の「>」をつけたまま、「Hello World」を入力して、プルダウンに表示された「Hello World」を選択します
1. コマンドパレット下に、情報タイプのメッセージボックスが開いて、「Hello World!」と表示されれば、成功です。
1. 新しいウィンドウを閉じて、終了させます

### ファイル詳細

#### package.json
まずは、定義を見ていきます。  
記述されているのは、名称や、バージョンなどの拡張機能の定義情報と、拡張機能でVS Codeやnpmで、使用する機能情報になります。  

重要な部分をピックアップして、もう少し、詳しく見ていきます。
```json:package.json
{
    "activationEvents": [
        "onCommand:extension.sayHello"
    ],
    "contributes": {
        "commands": [{
            "command": "extension.sayHello",
            "title": "Hello World"
        }]
    }
}
```

- activationEvents

拡張機能がロードされるタイミングです。「onCommand」が指定されているので、コマンド呼び出し時になります。  
コマンドの呼び出しは、contributesで指定されている定義で決定されます。  
今回の場合は、コマンドパレットから、コマンド「Hello World」が選択されたときに、extension.tsのactivateが**一度だけ実行**されて、VS Codeに拡張機能として取り込まれます。  

- contributes

ここで指定するのは、どのような時に、拡張機能を実行するのか指定します。  
今回の場合は、コマンド実行時で、「Hello World」が、指定されたときになります。  

「title」は、コマンドパレット内の表示情報となります。コマンドパレット内で、「Hello World」と検索できます。  
「command」は、実行するコマンドになります。「comannds」では、複数の「command」が指定できます。  
「src/extension.ts」で指定されている、「**registerCommand**」の引数のcomannd文字列を同じでなくてはなりません。実際のコードは、**extension.ts**で、指定します。  

「contributes」は、他にも「menu」や「ショートカットキー」、などが指定できます。  
[公式サイト Contribution Points](https://code.visualstudio.com/docs/extensionAPI/extension-points#_contributesmenus)

その他の「**package.json**」内の説明は、表にまとめました。  

|名称|必須|タイプ|説明|
|:--:|:--:|:--:|:--:|
|name|Y|string|拡張機能の名称。スペースなしの小文字|
|version|Y|string|バージョン。フォーマットは、「MAJOR.MINOR.PATCH」。<br /><ol><li>MAJOR 互換性がないAPIの変更</li><li>MINOR 後方互換性があり機能性を追加した</li><li>PATCH　後方互換性を伴うバグ修正をした</li><ol>|
|publisher|Y|string|公開者名。Marketplaceにアップするときに使用します。|
|engines|Y|object|VS Codeの最小バージョンを指定します。|
|displayName||string|Marketplaceで使用する拡張機能の表示名|
|description||string|拡張機能の説明|
|categories||string[]|拡張機能の使用するカテゴリを指定。値としては、[Languages, Snippets, Linters, Themes, Debuggers, Other]|
|activationEvents||array|拡張機能をロードするタイミングを指定。[onLanguage:${language}, onCommand:${command}, onDebug:${type}, workspaceContains:${toplevelfilename}, *|
|main||string|拡張機能のエントリーポイントを指定|
|contributes||object|拡張機能のコントリビューション|
|scripts||object|様々なタイミングで実行される script コマンド |
|devDependencies||object|開発時のテストやドキュメンテーションに利用する外部のフレームワークの設定|

[公式サイト Extension Manifest File - package.json](https://code.visualstudio.com/docs/extensionAPI/extension-manifest)  
[package.jsonの参考資料](http://liberty-technology.biz/PublicItems/npm/package.json.html:"参考")


#### extension.ts
では、拡張機能のコードを見ていきます。  

以下、がソースファイルの中身です。わかりやすいように、コード内に説明を入れています。

```ts
// strictモードの宣言
// コード内のエラー判定が厳しくなり、あいまいなコードに制限がかかる
'use strict';

// VSCodeのモジュールをインポート。
import * as vscode from 'vscode';

// 拡張機能を作成するときには、「activate」関数をexportする必要があります。
// ロード時に一度だけ呼ばれます。
export function activate(context: vscode.ExtensionContext) {

    // コンソール出力
    console.log('Congratulations, your extension "helloworld" is now active!');

    // vscodeモジュールのcommands.registerCommandの呼び出し
    // commandは、「extension.sayHellow」で、実行時には、
    // 「（） => 」引数なしで呼び出され、「vscode.window.showInformationMessage('Hello World!');」が実行されます。
    let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
        // 情報タイプのメッセージを表示
        // コマンドが実行されるたびに、extension.sayHelloが呼ばれて、この関数のみ実行される
        vscode.window.showInformationMessage('Hello World!');
    });

    // 拡張機能が使用されなくなったときに、リソース開放を行うための、設定。
    // 今回は、registerCommandの指定をを開放する必要があるので、開放対象に追加する。
    context.subscriptions.push(disposable);
}

// 拡張機能が非アクティブ化されたときに、実行されます。
export function deactivate() {
}
```

大事なことは、「**registerCommand**」で指定する「'extension.sayHello'」と、実行に必要なコールバックの関数です。  
コールバック関数の指定は、アロー関数式で、記述されている無名関数が、実行されるたびに呼ばれます。  
「'extension.sayHello'」は、package.jsonで指定されているものと同じでなくてはなりません。  
また、使用したリソースを開放するために、「context.subscriptions」に、「push」する必要があります。

> デバッグするとわかりますが、activate関数では、それぞれの設定のみが行われて終了します。  
> activate終了後に、「extension.sayHello」の実行が行われます。  
> また、activate関数は、最初のロード時にしか、呼び出されません。

## 備考
> 拡張機能で、「vscode-icons」を入れていたのですが、ファイルに対して権限が必要で、無視してました。
> デバッグ実行の時に、それが例外だして、実行できない状態でした。
> できるだけ、まっさらな環境で、作成したほうが、良さそうです。