VSCode のExtensionを開発してみる

こんにちは。
Visual Studio Code、使っていますか?
ぼくはこのエディタが大好きです。

今回は、このエディタの Extension(=プラグイン)の開発をしていきたいと思います。

※ node.js が必要です!

開発方法

  • TypeScript + Node.js でいけます。
  • ジェネレータは Yeoman でできています。

手順1: Yeomanを入れる。

npm install -g yo

また、VSCode Extension generatorも入れます。

npm install -g generator-code

手順2: ジェネレータを実行

> yo code
? ==========================================================================
We're constantly looking for ways to make yo better!
May we anonymously report usage statistics to improve the tool over time?
More info: https://github.com/yeoman/insight & http://yeoman.io
========================================================================== Yes

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

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


I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.

Extension の形式は、TypeScript がおすすめです。
開発するとき、インテリセンス君が強力に働いてくれて便利だからです。

その後、名前、識別子、説明、開発者名、Git と聞かれます。
今回は、Git の設定は n にして置きました。

ずらーとモジュールがインストールされます。

手順3: Extension を実行

> cd first-ext
> code .

code . がうまくいかない時は、
first-ext フォルダを VSCode で開いてください。

ジェネレータを実行した状態で、
Hello world が試せるようになっているので、
作った Extension を実行してみましょう。

左側のデバッグボタン(虫)をクリック、緑色の再生ボタンをクリックします。
(ちなみに F5 キーでもいけますー
すると、デバッグ用の VSCode がもう一つ起動します。

vscode-ext-debug

この状態で、Extension をテスト(=実行)できるわけです。

デバッグ用に開いた VSCode で、Ctrl + Shift + P をしてコマンドパレットを開きます。
Hello world と入力しEnter キーを押してみましょう。

vscode-ext-command

Hello world というメッセージが表示されます。

vscode-ext-info-message

この動きは、すべて今作った Extension で動いています。

では、どういう仕組みで動いているのか見てみましょう。

手順4: 動く仕組みは・・?

vscode-ext-nagare
↑ ざっくりとした流れの画像

いまデバッグしていたウインドウは閉じて、
先ほどのプラグインを開いたウインドウに切り替えましょう。

まず、コマンドパレットで「Hello World」と入力しましたね。
これはpackage.json の、contributes.commands から取得されています。

vscode-ext-package.json

ここでは、コマンドパレットでHello Worldが実行されたら、
extension.sayHelloを実行すると書いてあります。

では、この extension.sayHello はどこにあるのでしょう。
src/extension.ts を開いてみましょう。

vscode-ext-extension.ts

activate 関数の中に、
vscode.commands.registerCommand(...)を呼び出している行があります。(17行目)
ここでは、extension.sayHello コマンドが呼び出されたら、
第二引数のコールバック関数を実行する、となっています。

コールバック関数には、
vscode.window.showInformationMessage('Hello World!')
と書かれています。
これは、指定された文字列を先ほどのようなメッセージで表示する関数です。

手順5: 少し変えてみる

extension.ts を、少し変更してみましょう。

let disposable = vscode.commands.registerCommand('extension.sayHello', () => {
  // The code you place here will be executed every time your command is executed

  // Display a message box to the user
  vscode.window.showErrorMessage('Hello Error Message!');
});

似た関数で、showErrorMessage という物があります。
これは、エラーを表示する関数です。

実行してみましょう。手順は先程と同じです。
(F5キーでもいけるよ)

同じように、Ctrl+Shift+P から Hello World を選ぶと、

vscode-ext-show-err-msg

エラーメッセージ出現!

まとめ

このように、Extension は、
TypeScript + Node.js で開発できます。

また、公式サイトにも英語ですが、
Extension の作成方法が載っているので、
見てみてください。

TypeScriptのES6 Moduleでexport varしたものに書き込む

TypeScriptで、次のようにしたい時があります。

export var field1 = "foo";
import {field1} from "./export";
// エラーになる
field1 = "bar";

しかし、エラーになります。

Invalid left-hand side of assignment expression.

方法1:namespace で囲む

これは、変数を namespace(旧 module )で
囲ってあげることで解決できます。

export namespace fields {
  export var field1 = "foo";
}
import {fields} from "./export";
fields.field1 = "bar";

前は、namespace を、module と定義していましたが、
ES6 module と混同してしまうので、
これからは、namespace を使います。

方法2:setter となる関数を使う

var field1 = "foo";
export function setField1(text:string) {
  field1 = text;
}
import {setField1} from "./export";
setField1("bar");

まとめ

仕様がムズカシイ。
どうやら export default var とかもエラーになるから、
直接フィールドを export するのは良くないのかもしれないです。

TypeScriptの複雑な型

こんにちはー!!
TypeScriptには、名前の通り、「型」があります。
その中で、複雑な型もあるので、紹介したいと思います。

配列

2つの定義の方法があります。

var numArray: number[] = [0, 1, 3, 5, 10, 100, 42];
var numArray2: Array<number> = [0, 42];

// これも上と同じ
console.log(typeof numArray); // object
console.log(numArray instanceof Array); // true

連想配列

知らなかった!いままでは Object 型とか、any とか使っていました・・

var hash1: {[key: string]: string} = { "a1": "123", "b2": "def" };
var hash2: {[key: number]: HTMLElement} = { 1: document.getElementById("elem"), 42: document.getElementById("42") };

構文は、次のようになります。

{[key: インデックスの型]: 値の型}

応用: 2次元配列

次のような配列は、どんな型にしたらよいでしょう。

[["apple", "banana", "tomato"], ["fire", "water", "tree"]]

文字列の2次元配列です。
まず配列の中に配列があって、その中に文字列ということなので、次のように書きます。

var ar: Array<Array<string>>;

こんな書き方もできます。

var ar2: string[][];

応用: 配列内の連想配列

では、次のような場合はどうでしょう。

[{"name": "taro", "like": "apple"}, {"name": "hoge", "like": "pc"}]

配列の中に連想配列があるので、こんなのはどうでしょう。

var list = Array<{[key: string]: string}>;

こんな書き方もできます。

var list2 = {[key: string]: string}[];

型の別名を指定する

いちいち、{[key: string]: string} とか、入力するのきついので、
別名をつけちゃいましょー!!

type type1 = {[key: string]: string}; // 別名を指定
var list: type1 = { "first": "abc", "second": "def" };

type 型の別名 = もととなる型 で定義できます!

まとめ

TypeScriptには、numberとかの簡単な型もあるけれど、
連想配列とかは、Objectとするのではなく、インデックス、値の型まで指定できる!

さっきから「型」が「方」って変換されて困る!!

【感動】VSCodeのTypeScriptで、「Find Usages」相当の機能が使えた!

TypeScriptのIDEをNetBeansにした
こんにちはー。TypeScript、使っていますか?今まで、Visual Studio Code を、エディタとして使っていましたが、作っていたアプリが大きくなってきたので、NetBeansを使...

ここで書いたように、VSCodeでは、TypeScriptで
「Find Usages」という、
WebStormとかで、変数、関数などがどこで使用できるかを
プロジェクト内で検索し、表示してくれる機能がないと思っていました。

しかし、ありました!!!

VSCodeでは、Find All References というらしいです。

検索したい変数、関数などにカーソルを置き、Shift + F12
もしくは、右クリック→「 Find All References 」でもできます。

vscode-ts-find-usages

似ている機能で「Peek Definition」という、定義を吹き出しで表示してくれるものがあります。
こちらのショートカットキーは、Alt + F12 です。
今までは、これを使っていたし、これしかないと思っていましたが、
Shift + F12で、定義も表示されてくれるので、もう使わなくていいです^^

というわけで、また NetBeans から VSCode にもどりました。

【TypeScript】引数が違うメソッド同士をオーバーロード

TypeScriptで、次のようなオーバーロードをしたかったです。

function test(num: number, x: number, y: number): void; // 1
function test(num: number, pos: number[]): void; // 2

function test(num: number, pos?: number[], x?:number, y?: number): void {
  if (typeof x !== "undefined") {
    // 1のとき
  } else if (typeof pos !== "undefined") {
    // 2のとき
  }
}

しかし、エラーが出ました。
次のようにすればできます。

function test(num: number, x: number, y: number): void; // 1
function test(num: number, x: number[]): void; // 2

function test(num: number, x: any, y?: number): void {
  if (typeof x === "number") {
    // 1のとき
  } else if (x instanceof Array) {
    // 2のとき
  }
}

引数名を共通にすればできます。ちょっと違和感はありますが・・

TypeScriptのIDEをNetBeansにした

こんにちはー。
TypeScript、使っていますか?

今まで、Visual Studio Code を、
エディタとして使っていましたが、
作っていたアプリが大きくなってきたので、
NetBeansを使うことにしました!

TypeScript Editorプラグインを入れる

デフォルトでは TypeScript は使えないので、
プラグインを入れましょう!

https://github.com/Everlaw/nbts/releasesから、
「XXX.nbm」の最新版をダウンロードしましょう。

メニューバーの、ツールからプラグインを開きます。
Netbeans-tsplugin-install

  1. ダウンロード済み タブを開く
  2. プラグインの追加で、ダウンロードしたファイルを選択
  3. インストールし、IDEを再起動

補完を使いやすく

初期設定では、「 . 」を入力するまで、補完されません。
この設定を変えたいと思います。

  • ツールから設定を開く。

Netbeans-ts-setting

  1. エディタを選択
  2. コード補完タブへ
  3. 言語をJavaScriptにする
  4. 可能性のあるすべての状況に変更
  5. OKをクリック

便利な機能

宣言へ移動

これは、VSCodeでもあります。
宣言(定義されている場所)へ移動したい変数、プロパティなどにカーソルを置き、
Ctrl+B!!
もしくは、右クリックからナビゲート、宣言へ移動。
ファイルをまたがってもできます。

Ctrl+マウスポインタ

VSCodeでもあります。
変数名、型を取得できます。

使用状況を検索

これが一番やりたかった!
Visual Studio とかだと、開いてるファイル内でしかできないけど、
NetBeansはプロジェクト内でできる!!
変数にカーソルを置き、Alt+F7もしくは、右クリックから使用状況を検索。

netbeans-ts-usages

わーお!

2016/01/30 追記:
VSCodeでも、同じような機能がありました!

【感動】VSCodeのTypeScriptで、「Find Usages」相当の機能が使えた!
ここで書いたように、VSCodeでは、TypeScriptで「Find Usages」という、WebStormとかで、変数、関数などがどこで使用できるかをプロジェクト内で検索し、表示してくれる...

最後に

こんな感じに、大きなコードとかも楽に扱える便利な機能もあるので、
ぜひ使ってみてください!

TypeScriptで、関数を型にする

Typescriptの型として、
「number」「string」「boolean」などとありますが、
実は、関数も型にできます!

var function1:(arg1:number, arg2:string)=>boolean;

こんな感じ。

(引数名:型, 引数名:型 .....)=>戻り値

戻り値がないときは、voidを指定できます。

どんなときにつかうの?

コールバック引数を使いたい

function progressA(callback: (msg:string)=>void) {
  callback("hoge");
}

progressA((msg) => {
  console.log(msg);
});

関数を配列とする

var fnArray:Array<(msg:string)=>void> = [];
fnArray.push((msg) => {
  alert(msg);
});
fnArray.push((msg) => {
  console.log(msg);
});
fnArray.forEach(i => {
  // 関数だから呼び出せる
  i("Hello, TypeScript!");
});

AddEventListenerの引数にも!

lib.d.tsを見てみると、

addEventListener(type: "click", listener: (ev: MouseEvent) => any, useCapture?: boolean): void;

引数listenerの型にも使われています!

TypeScript楽しい!奥深い!

Gulpでtsify+watchify

Qiitaにもかきました。

Gulpでtsify+watchify - Qiita
TypeScriptでBrowserifyが使える、 (https://www.npmjs.com/package/tsify)を知ったのですが、 ビルドの時間が遅いし、毎回「gulp XXX」って打つのが大変な...

TypeScriptでBrowserifyが使える、 tsifyを知ったのですが、

ビルドの時間が遅いし、毎回「gulp XXX」って打つのが大変なので、

watchifyと一緒に使えないかな・・・と思いました。

次のようなgulpfile.jsを作りました。

  • gulp watchify-tsifyと実行する。
  • ./ts/a.ts./ts/b.tsを監視する。
  • watchifyでupdateが発生するたびに、runBundle()を呼び出す。
  • b.pluginでtsifyを読み込み、js/all.jsを出力する。

参考

Gulp + browserify + watchify + gulp-sass で自動高速コンパイル環境
Gulp とはGulp は Web 制作に関するいろいろな作業を自動化するためのツールです。Sass のコンパイルや Gulp の使用には node.js や Sass などのインストールが必要なの...