ざっくりとイメージをつかむための Visual Studio Code 拡張機能開発入門

試しに Visual Studio Code 拡張機能開発でタスク管理ツールをつくってみた。

- [tritask/tritask-vscode: VSCode 版 Tritask](https://github.com/tritask/tritask-vscode)

拡張機能の開発方法はドキュメント [Visual Studio Code Extension API](https://code.visualstudio.com/api) を熟読すれば事足りるが、英語だし、そもそも前提知識ないしで色々苦労したので、備忘録兼ねて要点をまとめておきたい。

# 前提
- Windows 10 を使っていること

MacOS 10.14(Mojava) でも開発はしていますが、基本的に Windows 使いなので Windows を前提とさせてください。

- Visual Studio Code を使っていること

コマンドパレットは何か、など Visual Studio Code の基本的な使い方はわかっているものとします。

# 拡張機能開発環境セットアップ

## node.js と npm
node.js をインストールする。公式サイトからインストーラーをゲットして実行する。

npm をインストールする。node.js に付属している。

終わったら動作確認。

```
$ node -v
v10.16.0

$ npm -v
6.9.0
```

## npm から Yeoman
npm から yo と generator-code をインストールする。

```
$ npm install -g yo
$ npm install -g generator-code
```

これは Yeoman というテンプレートジェネレータと、VSCode 用の Yeoman テンプレートデータを入れている。これで `yo code` コマンドを実行するだけで Hello World プロジェクトを簡単につくれるようになる。

# プロジェクトの作成
`yo code` コマンドを実行する。

何をつくるかを選べる。

- New Language Support(言語定義)
- New Color Theme(テーマ)
- New Extension(拡張機能)

つくるものを選んだら、さらにプロジェクト名や作成者名などを入力させられる(あとで変えられる)。これも終わったら、プロジェクトファイル一式がつくられるので、あとはそのディレクトリに移動して VSCode を起動すればよい。

```
$ yo code
(new-extension-hoge をつくったとする)

$ cd new-extension-hoge

$ code .
```

## ハマリどころ: Proxy 環境下だとキツイです
Proxy がガッツリ入っているようなキツイ環境下だと、yo code が動かない。

「ちゃんと HTTP_PROXY 環境変数やら npm config set proxy %HTTP_PROXY% やら VSCode settings.json の http.proxy やら設定すれば行けるのでは？」と思いがちだけど、そう甘くもない。

(これは推測だが)そもそも昨今の OSS は依存関係等で迷宮みたいに通信走らせているので、キツイ環境下だと何かしらひっかかってよくわからんエラーで上手く動かない率が高い。

最後に私が試行錯誤して見つけた情報源の一部を置いておく。

- [ProxyResolver#getCertificates depends on maxBuffer length · Issue #72844 · microsoft/vscode](https://github.com/microsoft/vscode/issues/72844)
- [[json] schema not loading behind working proxy everything else works fine · Issue #74991 · microsoft/vscode](https://github.com/microsoft/vscode/issues/74991)
- [node.js - How to fix RequestError: Error: connect ETIMEDOUT while running Mocha JS/NodeJS test from behind proxy - Stack Overflow](https://stackoverflow.com/questions/44901418/how-to-fix-requesterror-error-connect-etimedout-while-running-mocha-js-nodejs)
- [[Bug] Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue. · Issue #72607 · microsoft/vscode](https://github.com/microsoft/vscode/issues/72607#issuecomment-496312994)
- [proxy - unable to get local issuer certificate vscode - Stack Overflow](https://stackoverflow.com/questions/34921875/unable-to-get-local-issuer-certificate-vscode)
- ……

# 開発手順
開発、テスト、デバッグ、公開までの流れや大まかな手順をざっくりと。

## 初回時
他のリポジトリから clone した場合など、最初は「そのプロジェクトでの開発に必要な npm パッケージ群」がインストールされていない。

```
$ cd (拡張機能のプロジェクト)
$ npm install
```

でインストールしてやる。

これを行うと node_modules というディレクトリができて、ここにパッケージ群が入る（容量でかい）。

## 開発時
VSCode を開く。

```
$ code .
```

ドットは必要。「カレントディレクトリを基点にして code(VSCode) を開きますよ」という意味になる。

VSCode が開いたら、VSCode 上でコードを書いていく。

## デバッグ実行
F5 を押す。

新たな VSCode ウィンドウが立ち上がるはず。

デバッグログなどは元々の VSCode ウィンドウ側のデバッグコンソール上で見れる。コードに書いた console.log() の出力もここで見れる。

## ユニットテスト
今回試してないのでよくわからん。

## パッケージング
vsce というツールが必要。npm でインストールする。

```
$ npm install -g vsce
```

パッケージングは以下で実行できる。

```
$ vsce package
```

これを行うと .vsix というバイナリファイルが生成される。

(!) Proxy 環境などキツイ環境下では vsce package が動作しない(ブロッキングして応答が返ってこない)ことがある。

## .vsix ファイルをインストールする
vsix ファイルのインストールは Command Palette > Install from VSIX あるいは `code --install-extension xxxxx.vsix` コマンドで行える。

インストールしたらサイドバー > 拡張機能 > @installed と入力して、当該拡張機能がインストールされているか確かめる。また、有効になっている拡張機能は @enabled でわかる。

アンインストールは、VSCode 上でやるなら上記 @installed で表示したところからギアアイコン > Uninstall を。コマンドでやるなら `code --uninstall-extension xxxxx.vsix` を。

ちなみに、たまに「(新しく .vsix をつくりなおして再インストールしても)反映されないやんけ」なんてことが起きたりするので、(特に何度も再インストールを繰り返す場合は) VSCode を再起動した方が良い。 **「なんか反映されてないな」と思ったら、とりあえず再起動。これ大事**。

## MarketPlace に公開する
Azure アカウントが必要らしい。試してないのでわからん。

# 何をつくるべきか ～Language Support と Color Theme と Extension の違い～
ややこしいのでこれらの違いは押さえておきたい。

## Language Support
言語定義。

「XXXX 言語は拡張子が .xxx で、文法はこんな感じで、……」みたいなことを定義する。文法を定義するとは、トークンとスコープを定義すること。

トークンとは「特定の意味を持つ文字列パターン」。正規表現で書く。

スコープとはトークンにつける名前。xxx.yyy.zzz みたいに階層的に書く。詳細は後述するが、スコープには命名規則（慣習）があって、この慣習に従わないとハイライトされない（VSCode のテーマはこの慣習スコープ名を元につくられているため）。

参考:

- [Syntax Highlight Guide | Visual Studio Code Extension API](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide)
  - 読めるならちゃんと読む
  - 結局は公式ガイド読んで手動かすのが一番確実
- [vscode/extensions/markdown-basics at master · microsoft/vscode](https://github.com/microsoft/vscode/tree/master/extensions/markdown-basics)
  - VSCode にデフォで入っている Markdown の言語定義
  - syntaxes/markdown.tmLanguage.json を見ると良い

## Color Theme
テーマ。

「このスコープはこの色とスタイルで表示します」みたいなことを定義する。対応しているのは foreground(文字色)、background(背景色)、fontStyle(bold italic underline の三種類のみ)。

一つ厄介なのは、テーマが **VSCode 全体に波及する設定である** ということ。もっと言えば、こんな事情がある。

- テーマは VSCode 全体に波及する設定なので、好き勝手にスコープを使われると困る
- なので VSCode では [慣習](https://macromates.com/manual/en/language_grammars) に従ってスコープ名を付けてね、としている
- その結果、テーマ作成者は **慣習に従ったスコープに対する定義だけつくれば良くなる**

で、その慣習というのが TextMate で使われている [Naming Conventions](https://macromates.com/manual/en/language_grammars) というやつ(12.4 項までスクロールしてください)。いくつか例をあげると、

- コメントは comment.line とか comment.block を使え
- 定数は constant.numeric とか constant.character とか使え
- ……

こんな感じになっている。

参考:

- [vscode/extensions/theme-monokai/themes at master · microsoft/vscode](https://github.com/microsoft/vscode/tree/master/extensions/theme-monokai/themes)
  - VSCode にデフォで入っている monokai テーマのソース
  - themes/monokai-color-theme.json を見ると良い

## Extension
拡張機能。

「このショートカットキーでこんな操作を呼び出す」とか「このメニューにこんな項目を追加したら、選択したらこんな操作をする」のように「呼び出し方」と「操作」を作り込むイメージ。

「呼び出し方」については、package.json に json で定義をゴリゴリ書いていく。

「操作」は、ガチでプログラミングする部分。[VSCode API](https://code.visualstudio.com/api/references/vscode-api) を駆使する。たとえば「こんなフォーマットの文字列を、現在のカーソル位置に挿入したい」操作がしたい場合は、以下のような感じになる。

- 現在のカーソル位置を取得(window.activeTextEditor.selection.active)
- 挿入したい文字列をつくる
- 現在のエディタに対して edit を実行(window.activeTextEditor.edit)

「操作」はコマンドという単位でつくる。平たく言えば、こんな感じ。

```typescript
	let completion_simple = vscode.commands.registerCommand('tritask.task.add', () => {
		addTask();
	});
```

これは tritask.task.add というコマンドを登録している。処理は addTask() 関数。この中で VSCode API を呼び出してタスク追加的な処理を実現する感じ。

で、「呼び出し方」は、この tritask.task.add に対して定義する。package.json にこんな感じで書く。

```json
    ...
    "menus": {
      "commandPalette": [
        {
          "command": "tritask.task.add",
          "when": "resourceExtname == .trita" // ★拡張子 .trita ファイルの時のみコマンドパレットに表示する
        },
    ...
    "keybindings": [
      {
        "command": "tritask.task.add",
        "key": "alt+a",  // ★ Windows では Alt + A で呼び出せるようにする
        "mac": "alt+a",  // ★ Mac では Option + A で呼び出せるようにする
        "when": "resourceExtname == .trita" // ★このショートカットキーは .trita ファイルでのみ有効
      },
    ...
```

## Q: .xxx という拡張子で独自文法をサポートしたい
→ Language Support をつくりましょう。

ただしスコープ名は慣習に従いましょう。そうしないと既存のテーマで色付けされません。

## Q: Language Support で独自のスコープ名を使いたい
→ 方法は二つあります。

一つ目は、独自スコープ名に対する色を定義したテーマ(Color Theme)を自作することです。

ただし、そのようなテーマを有効にすると、(あなたのテーマには慣習のスコープ名に対する定義がないので)大部分の既存のシンタックスハイライトがカラーリングされなくなります。たとえば json も Markdown も、Javascript も TypeScript も、ハイライトがすべて消えるでしょう。なぜなら、これらの言語定義は慣習のスコープ名に則っているからです。これを防ぎたいなら、あなたのテーマに「慣習のスコープ名に対する色の定義」をつくらねばなりません。

二つ目は、独自スコープ名に対する色の定義を **利用者の settings.json に書かせる** 方法です。冒頭で紹介した [Tritask](https://github.com/tritask/tritask-vscode) は、この方法を用いています。

## Q: 文字列加工やカーソル移動など便利操作を拡張機能としてつくりたい
→ Extension をつくりましょう。

[VS Code API](https://code.visualstudio.com/api/references/vscode-api) と戦うことになります。VS Code API がサポートしてないことは当然ながら実現できません。

ただし、拡張機能の開発プラットフォームは node.js ですから、npm で各種ライブラリを使えば割と色々なことができます。たとえば [Child Process](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) を使えば任意のコマンドラインを実行させることができます。

## Q: .xxx という独自拡張子の独自文法に、独自操作を追加したい
→ Language Support と Extension をつくりましょう。

プロジェクトは二つになります。

# 開発中にお世話になるドキュメントやソース
基本的に [Extension API | Visual Studio Code Extension API](https://code.visualstudio.com/api) この公式ドキュメントを読み込めば OK。

あとは VSCode のソース [vscode/extensions at master · microsoft/vscode](https://github.com/microsoft/vscode/tree/master/extensions) も例が豊富なので参考になる。私は個人的に馴染みのある Markdown、Bat(バッチファイル)、Python あたりをよく読んだ。

それから特にトークンやらスコープ名やら書く時に [Language Grammars — TextMate 1.x Manual](https://macromates.com/manual/en/language_grammars) もお世話になる。

個人的には以下もよく見た。

- [Syntax Highlight Guide | Visual Studio Code Extension API](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide)
  - トークンとかスコープ名とか、その辺の基礎が書いてある
- [Visual Studio Code Key Bindings](https://code.visualstudio.com/docs/getstarted/keybindings)
  - そもそもショートカットキーについてよくわかってなかったのでこれ読んで勉強した(WinとMacでどう違うか等)
- [VS Code API | Visual Studio Code Extension API](https://code.visualstudio.com/api/references/vscode-api)
  - エディタの内容取得、編集、カーソル移動など。
- [Contribution Points | Visual Studio Code Extension API](https://code.visualstudio.com/api/references/contribution-points)
  - packege.json の書き方
  - menus で右クリックメニューに項目を追加するとか、keybindings でショートカットキーを定義するとか
  - **VSCode 拡張機能開発は割と packege.json 編集ゲーなところがある** ので、しっかり読んで、試して把握しておくと楽できる
  - ※気力尽きたので本記事では細かい解説はしません……

# Language Support 開発メモ
今回私は .trita ファイルをハイライトする Language Support をつくった。tmLanguage.json の書き方を簡単にまとめておく。

※ただし一般的なプログラミング言語ほどガチの定義ではなく、あくまでオレオレデータファイルを簡単に定義しただけなので注意。それでもこの例を見れば「オレオレ文法はこうやれば作れそう」ってのがなんとなくわかると思う。

## syntaxes\trita.tmLanguage.json の書き方(全体構造)
以下のようになっている。

```json
{
	"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
	"name": "Tritask",
	"scopeName": "text.trita",
	"patterns": [
		{
			"include": "#line-with-separator"
		},
		...
	],
	"repository": {
		"line-with-separator": {
			"patterns": [{
				"name": "line.separator.trita",
				"match": "^(.+)(\\-{2,})(.+)$"
			}]
		},
		...
	}
}
```

メインは .patterns と .repository。

.patterns では「こんなトークンが登場しますよ」の定義を列挙する。

.repository では各トークンのマッチ条件と名前(スコープ名)を列挙する。

## syntaxes\trita.tmLanguage.json の書き方(トークン定義 その1)
たとえばこれは、

```json
		"line-with-separator": {
			"patterns": [{
				"name": "line.separator.trita",
				"match": "^(.+)(\\-{2,})(.+)$"
			}]
		},
```

以下のような意味になる。

- トークン名：line-with-separator
- このトークンとみなされる文字列パターン: `"^(.+)(\\-{2,})(.+)$"`
  - 意味: `-` が二つ以上並んだ行
- このトークンのスコープ名: `line.separator.trita`

ちなみに色の定義はない。

色の定義はテーマ側で行うことになる。この場合、line.separator.trita に対する色はこれだ！という定義をテーマ側で行うことになる。もちろん、この line.separator.trita というスコープ名は私が書いたオレオレスコープ名であり、慣習に従っていないので、既存のテーマでは色付けされない。

## syntaxes\trita.tmLanguage.json の書き方(トークン定義 その2)
もう少し複雑な例を。

```json
		"time": {
			"patterns": [{
				"match": "\\b([0-9]{2}:[0-9]{2}) ([0-9]{2}:[0-9]{2})\\b",
				"captures" : {
					"1" : {
						"name" : "time.start.trita"
					},
					"2" : {
						"name" : "time.end.trita"
					}
				}
			}]
		},
```

これは以下のような意味になる。

まず、

- トークン名：time
- このトークンとみなされる文字列パターン: `"\\b([0-9]{2}:[0-9]{2}) ([0-9]{2}:[0-9]{2})\\b"`
  - 意味: `hh:mm hh:mm` という文字列
  - ちなみに `\\b` は単語区切りの意

となっているが、このトークンのスコープ名がちょっと複雑になっている。それが captures の部分で、

- 1 の部分は time.start.trita というスコープ名に
- 2 の部分は time.end.trita というスコープ名に

という意味になっている。キャプチャについては正規表現を勉強していただきたいが、一言で言えば `()` で囲った部分を指す概念で、順に 1, 2, 3, ... と番号が振られる。

captures を使えば「time には time start と time end がある」みたいな階層的な文法を簡単に定義できる。

# Extension 開発メモ
今回私は .trita ファイルに対する操作体系を Extension で作り込んだ。結果として、Alt + A でタスク行を挿入したり、Alt + C で複製したり、Alt + Z で並び替えしたり、といったことができるようになった。

この節では Extension 開発時に書いたソースに関する、いくつかの解説を行う。

※言語は TypeScript を想定する。

## エントリーポイント
いわゆる main 関数にあたる部分。yo code した時にちゃんと定義されているが、一応見てみる。

```typescript
export function activate(context: vscode.ExtensionContext) {

	let task_add = vscode.commands.registerCommand('tritask.task.add', () => {
		addTask();
	});
	...

	context.subscriptions.push(
		task_add,
		...
	);
}
```

やることは単純で、activate() 関数内でコマンド登録と自動解放登録をする。

コマンド登録は vscode.commands.registerCommand()。パラメータとしてコマンド名 xxx.yyy 形式の文字列と、処理の中身を関数で与える。

自動解放登録はregisterCommand() の戻り値を context.subscriptions.push() にセットしていけば良い。いわゆるデストラクションの登録である。

## 何よりもまずは「今開いているエディター」を取得する
拡張機能でやることと言えば「エディタ上の内容を編集する」「カーソルを動かす」が二大メインとなるだろう。いずれにせよ、「今開いているエディター」を操作するためのオブジェクトを手に入れる必要がある。vscode.window.activeTextEditor がこれに相当する。

関数化しておくと楽だと思う。

```typescript
function getEditor(){
	let editor = vscode.window.activeTextEditor;
	if(editor == null){
		abort("activeTextEditor is null currently.")
		throw new Error();
	}
	return editor;
}
```

リファレンスはこの辺。

- [window.activeTextEditor](https://code.visualstudio.com/api/references/vscode-api#window)
- [TextEditor 型](https://code.visualstudio.com/api/references/vscode-api#TextEditor)

## カーソルを動かす(内蔵コマンド)
動かし方は二種類ある。

一つ目は、VSCode 内蔵コマンドを呼び出すこと。

以下は golinetop(行頭に移動)、golineend(行末に移動)、left(←)、up(↑) を定義している。

```typescript
class CursorMover {
	static golinetop() {
		vscode.commands.executeCommand("cursorLineStart");
		return this;
	}

	static golineend() {
		vscode.commands.executeCommand("cursorLineEnd");
		return this;
	}

	static left() {
		vscode.commands.executeCommand("cursorLeft");
		return this;
	}

	static up() {
		vscode.commands.executeCommand("cursorUp");
		return this;
	}
}
```

内蔵コマンドについては File > Preferences > Keyboard Shortcuts から探すと良い。たとえば「cursor」で検索してみると、cursor に関するコマンドが多数ヒットする。まあコマンド名だけ見てもわかりづらかったりするのだが。

## カーソルを動かす(絶対指定)
内蔵コマンドでできそうにないなら、自力で頑張るしかない。

まずカーソル位置を変えるには vscode.window.activeTextEditor.selection に(移動先の位置を定義した)Selection 型のオブジェクトを代入すれば良い。

```typescript
let editor = getEditor();
editor.selection = (ここにSelection型オブジェクトを指定)
```

では Selection オブジェクトはどうやってつくるのか。[vscode-api#Selection](https://code.visualstudio.com/api/references/vscode-api#Selection) を見ていただきたいが、Position 型を二つ指定すれば良い。ややこしいが、Position 型は「ある一つの位置」を表すのに対し、Selection は「ある一つの範囲選択」を表す。Selection には「範囲選択の開始位置」と「終了位置」の二つが必要なので、Position 型を二つ指定している。

では Position 型はどうつくるか。[vscode-api#Position](https://code.visualstudio.com/api/references/vscode-api#Position) を見ていただきたいが、line と character から成る。

- line: n行目
- character: (line行目の)character文字目

となっている。たとえば Position(3, 20) なら 3 行目の 20 文字目だ。ただし 0 オリジンなので実際は 4 行目の 21 文字目である。

ともあれ、これで準備が整った。カーソルを4 行目の 21 文字目に動かす例を見てみよう。

```typescript
let editor = getEditor();
let destPos = new Position(3, 20);
let destSel = new Selection(destPos, destPos); // 範囲選択しないなら同じ Position を指定すれば良い
editor.selection = destSel;
```

## カーソルを動かす(相対指定)
カーソルを **今のカーソル位置から相対的に動かしたい** ニーズもある。可能だ。

要は「今のカーソル位置を示す Position」を取得した後、「その値のベースにして定義した相対位置」を指定した Position を作れば良い。

以下のようになる。

```typescript
// 現在位置の行頭を表す Position.
let editor = getEditor();
let curPos = editor.selection.active;
var newY = curPos.line; // 行番号は変えない
var newX = 0;           // 何文字目かを変える。行頭は 0 文字目なので 0。
let newPosWithRelative = curPos.with(newY, newX);
```

editor.selection.active で今のカーソル位置を取得する。Position.with() で当該 Position オブジェクトの複製(clone)をつくる。

## 内容を編集する
手順は意外とシンプルで、以下のようになる。

```typescript
let editor = getEditor();
let f = function(editBuilder: vscode.TextEditorEdit): void{
	editBuilder.insert(挿入先の位置をPositionで, 挿入したい文字列をstringで);
}
editor.edit(f);
```

[vscode-api#TextEditor](https://code.visualstudio.com/api/references/vscode-api#TextEditor) の METHOD の項にある edit() を見ていただきたい。「こういう編集をしてください」というコールバック関数を与えるようになっている。

で、その編集方法を指定する作法が [vscode-api#TextEditorEdit](https://code.visualstudio.com/api/references/vscode-api#TextEditorEdit)。メソッドは四種類用意されている。

- insert: 指定位置に指定文字列を挿入
- delete: 指定範囲を削除
- replace: 指定範囲を指定文字列に置換
- setEndOfLine: 改行コードを指定コード(CRLF か LF のどちらかのみ指定可)に変換

ラクチンだ。

## Q: editor.edit() した後、さらに処理したい場合はどうすれば？
ここで一つハマることがある。

**editor.edit() 後に書いた処理(カーソル移動や別の TextEditorEdit 処理)がなぜか効かない** という現象だ。

たとえば以下は正しく動作しないだろう。

```typescript
let editor = getEditor();
// 何か挿入して、
let f = function(editBuilder: vscode.TextEditorEdit): void{
	editBuilder.insert(挿入先の位置をPositionで, 挿入したい文字列をstringで);
}
editor.edit(f);
// その後で削除して、
f = function(editBuilder: vscode.TextEditorEdit): void{
	editBuilder.delete(削除位置をSelectionで);
}
editor.edit(f);
// 最後にカーソルを動かす
editor.selection = (移動先のSelection)
```

おそらく最初の insert しか効かないはず。

なぜこんなことが起こるかというと、edit() が非同期的なメソッドだからだ。つまり edit() の次に書かれたコード = edit() 実行完了後に実行されるはずのコード、という等式は **成立しない**。

「edit() 実行完了後」をきちんと知るためには、さらにお作法が必要となる。それが Thenable 型である。

結論を言うと、こんな感じで書けば良い。


```typescript
let editor = getEditor();
let f = function(editBuilder: vscode.TextEditorEdit): void{
	editBuilder.insert(挿入先の位置をPositionで, 挿入したい文字列をstringで);
}
let thenAfterInsert = editor.edit(f);
thenAfterInsert.then(
  (isSucceededEditing) => {
    if(!isSucceededEditing){
      return; // なんか失敗してるのでとりあえず中止しとく.
    }
    // ★ここが insert 完了後に実行される場所 ★
  }
)
```

Thenable は promise という「Javascript で非同期処理を楽に書く仕組み」をラップしたもので、**then() の中に実行完了後の処理を書いてね** というものだ（と思ってる。勉強不足で正直まだよくわかってない）。

※ちなみに処理を重ねたい場合は、ネストする必要はなく then(...).then(...) みたいにメソッドチェーンすればいいらしい。まだ試してないが。参考 → [【JavaScript入門】誰でも分かるPromiseの使い方とサンプル例まとめ！](https://www.sejuku.net/blog/52314)

## Q: (余談) なんで edit() は Thenable とかいう面倒くさそうな仕組みになっている？
想像だが、edit() がすぐに終了するとは限らないからだと思う。

edit() をすると、内部的には VSCode が色んな処理を走らせている。処理が一瞬で終わる保証がない。もし、これを同期的な仕組みにしてしまうと「なんか処理が遅いんだけど」となってしまう。極論、edit() を 10 回実行するコードがあったとして、1 回に 0.05s かかるとしたら、ユーザー側は全部終わるのに 0.5 秒待たないといけない。ストレスだ。

```typescript
func() // edit() を 10 回くらい使ってて 0.5s かかる
// ★1ここに来るまでに 0.5s 待つことになる
```

これが Thenable であれば、待つ必要はない。一方で「edit() の方は非同期で走らせてて、終わったら随時次の処理をするよー」という感じで、並行で走る。

```typescript
func() // edit() を 10 回使っているが Thenable で実装されている
// ★2ここには一瞬で来る
```

つまり ★2 の位置に来た時、**func() の処理が完了しているとは限らない** が、少なくとも **func() の処理が終わる 0.5s まで待たされることはない** のである。

……まあ非同期という概念のお話ですね。私もまだまだ勉強不足なので追々勉強していきます。

## ライブラリを使う
node.js ベースなので npm でインストール可能なライブラリが色々使える。

例1: 日付時刻操作を簡単に行う [moment.js](https://momentjs.com/)

```typescript
var moment = require('moment');
moment.locale("ja");

class DateTime {
	private _momentinst: any
	private _format: string

	public constructor(){
		this._momentinst = moment();
		this._format = 'YYYY/MM/DD';
	}

	public toString(){
		return this._momentinst.format(this._format);
	}
}

class DateTimeUtil {
	static todayString(): string {
		var dtobj = new DateTime();
		return dtobj.toString();
	}

	static nowtimeString(): string {
		return moment().format("HH:mm");
	}
}
```

DateTimeUtil.todayString() を呼び出せば 2019/08/20 ← こんな文字列が取れるようになる

例2: コマンドラインを実行する [child_process](https://nodejs.org/api/child_process.html)

```typescript
const exec = require('child_process').exec;

function doSort(){
	let commandLine = `python -i helper.py --sort`;

	exec(commandLine, (err:any, stdout:any, stderr:any) => {
		if(err){
			console.log(err);
		}
	});
}
```

doSort() を実行すると `python -i helper.py --sort` が実行される。

……と、こんな具合で、ライブラリ次第で相当なことができる。他にも [ファイル操作やクリップボード操作やらできるみたい](https://qiita.com/RAWSEQ/items/7c53596754d2a102499f)。

# おわりに
だいぶ荒削りですが、Visual Studio Code で拡張機能を開発してみたい皆様の参考になりましたら幸いです。

色々拙いと思うのでツッコミ歓迎です！

p.s. package.json まわりの解説をもうちょっと増やしたいですが力尽きたので、気が向いたら……。ドキュメントは [Extension Manifest](https://code.visualstudio.com/api/references/extension-manifest) この辺です。