【拡張機能の作り方】Visual Studio Code（VSCode）のスニペット作成が面倒だったので、自作の拡張機能をインストールした。

# 手順のまとめ

1. Node.js、npm、Gitをインストール
2. npm install -g yo generator-code を実行
3. npm install -g vsce　を実行
4. yo code を実行、対話形式で回答してVSCode拡張機能の雛形生成
5. vsce package を実行し、VSIXファイルを生成
6. VSCode上からコマンドパレットで「Extensions: Install from VSIX...」を実行、VSIXを選択して拡張機能をインストール

これだけで理解できる方は以下の解説は不要です。

# 拡張機能を作ろうと思ったきっかけ

スニペットはとても便利です。
コードを1文字ずつ書くことなく、秒でコードの塊を入力できます。
スニペットを使わずにコピペする方法も考えられますが、
アプリケーションのコマンドから呼び出して入力できる、これが非常に快適なのです。

ただしVisual Studio Codeのスニペットは作成が面倒です。jsonファイルで管理していることから下記調整が必要です。

- 「"」で囲む。
- もともとの「"」や「$」はエスケープ。
- 複数行をスニペット化しようとすると、1行1行「"」で囲むか改行を変換する。

骨が折れます。
これではメンテが面倒で結局使わないということにもなりかねません。

こんな作業は同じVSCodeの機能で解決するべき！
そう思ってエクステンションを作ってみました。
この記事では、VSCodeのエクステンションの作成方法について解説します。
※マーケットプレイスへの公開は考慮していません。オレオレ拡張機能の作成方法です。

#作ろうとしているもの

- テキストを選択してコマンドパレットから「SnippetConverter: to VSCode format.」を選択すると、各行をダブルクォーテーションで囲むなど、スニペットファイルで読み込める形式に変換する。
- テキストを選択してコマンドパレットから「SnippetConverter: to Source.」を選択すると、各行をダブルクォーテーションを取り払うなど、もとのテキストに戻す。

# ツールのインストール

## Node.js、npm、Gitをインストール

前提として、これらのツールをあらかじめインストールしておく必要があります。
インストール方法は何通りか存在するのでここでは詳細は省略します。
（私はMacのHomebrewを使ってインストールしました。）

Node.jsは、[公式サイト](https://nodejs.org/ja/)でインストールできます。
Node.jsをインストールすればnpmもインストールされます。
（Node.js、npmのインストール方法については、[npmの基礎知識 \- Qiita](https://qiita.com/sfp_waterwalker/items/91c6e8003da999821ecc)で記事を書いています。）

Gitはインストール方法がドキュメントで解説されています。
[Git \- Gitのインストール](https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)

## 拡張機能の雛形生成ツールをインストール

Yeomanという雛形生成ツールがあります。
Yeomanで雛形を生成するときには、ジェネレーターが必要になりますが、これもgenerator-codeというVSCode用のジェネレーターが存在します。
Node.js製なので、npmでこれらをインストールします。
（Yeomanからgenerator-codeという、VSCodeのジェネレータを呼び出してVSCode拡張機能の雛形を生成する仕組みです。）

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

## vsceをインストール

VSCodeの拡張機能をインストールしたり公開したりする場合にvsceというNode.js製のツールが必要になるのでインストールします。

```
npm install -g vsce
```

# 雛形を生成する

## Yoemanを実行してVSCode拡張機能の雛形を生成する

「yo code」とコマンドを実行させて雛形を生成します。
対話形式で質問に答えていきます。
ここでは下記のように回答しています。

```
? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? snippetconverter
? What's the identifier of your extension? snippetconverter
? What's the description of your extension?
? Initialize a git repository? Yes
? Which package manager to use? yarn
```

こんな感じで出力されると思います。

```
   create snippetconverter/.vscode/extensions.json
   create snippetconverter/.vscode/launch.json
   create snippetconverter/.vscode/settings.json
   create snippetconverter/.vscode/tasks.json
   create snippetconverter/src/test/runTest.ts
   create snippetconverter/src/test/suite/extension.test.ts
   create snippetconverter/src/test/suite/index.ts
   create snippetconverter/.vscodeignore
   create snippetconverter/.gitignore
   create snippetconverter/README.md
   create snippetconverter/CHANGELOG.md
   create snippetconverter/vsc-extension-quickstart.md
   create snippetconverter/tsconfig.json
   create snippetconverter/src/extension.ts
   create snippetconverter/package.json
   create snippetconverter/tslint.json


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


yarn install v1.16.0
warning package.json: No license field
info No lockfile found.
warning snippetconverter@0.0.1: No license field
[1/5] 🔍  Validating package.json...
warning snippetconverter@0.0.1: No license field
warning snippetconverter@0.0.1: The engine "vscode" appears to be invalid.
[2/5] 🔍  Resolving packages...
[3/5] 🚚  Fetching packages...
[4/5] 🔗  Linking dependencies...
[5/5] 🔨  Building fresh packages...
success Saved lockfile.
warning Your current version of Yarn is out of date. The latest version is "1.21.1", while you're on "1.16.0".
info To upgrade, run the following command:
$ curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
✨  Done in 10.18s.

Your extension snippetconverter has been created!

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

     cd snippetconverter
     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.




```

# 拡張機能の開発

ここまでの作業でVSCode拡張機能の雛形が生成されました。
拡張機能の名前（ここでは「snippetconverter」）と同名のディレクトリができています。
このディレクトリ内のファイルに編集をかけて、拡張機能を作っていきます。

このディレクトリをVSCodeで開きます。
下記のように、カレントディレクトリで「code .」とすると開くことができます。
※codeコマンドがインストールされていないと開けません。
このときは、VSCodeのコマンドパレット（Command + Shift + P）で
「Shell Command: Install 'code' command in PATH」を実行してインストールします。

[ターミナルからVisual Studio Codeを起動する方法【公式の方法】 \- Qiita](https://qiita.com/naru0504/items/c2ed8869ffbf7682cf5c)

```
cd snippetconverter
code .
```

ここからはVSCodeで編集していきます。今回編集したファイルは以下です。

- tsconfig.json（パッケージ自体の情報。コマンドの定義もここで行う。）
- src/extension.ts（実際の処理を記述。）
- README.md（これがそのままだと、後述のパッケージファイルが作成できない。）

作り方自体は省略しますが、以下のようにファイルを調整しました。
※拡張機能の作成方法は、ディレクトリの中にあるvsc-extension-quickstart.mdに記載されています。

```:tsconfig.json
{
	"name": "snippetconverter",
	"displayName": "SnippetConverter",
	"publisher": "username", <- 実際はユーザー名が入ります。
	"description": "",
	"version": "0.0.1",
	"engines": {
		"vscode": "^1.37.0"
	},
	"categories": [
		"Other"
	],
	"activationEvents": [
		"onCommand:extension.toVSCode",
		"onCommand:extension.toSource"
	],
	"main": "./out/extension.js",
	"contributes": {
		"commands": [
			{
				"command": "extension.toVSCode",
				"title": "SnippetConverter: to VSCode format."
			},
			{
				"command": "extension.toSource",
				"title": "SnippetConverter: to source."
			}
		]
	},
	"scripts": {
		"vscode:prepublish": "npm run compile",
		"compile": "tsc -p ./",
		"watch": "tsc -watch -p ./",
		"pretest": "npm run compile",
		"test": "node ./out/test/runTest.js"
	},
	"devDependencies": {
		"@types/glob": "^7.1.1",
		"@types/mocha": "^5.2.6",
		"@types/node": "^10.12.21",
		"@types/vscode": "^1.37.0",
		"glob": "^7.1.4",
		"mocha": "^6.1.4",
		"typescript": "^3.3.1",
		"tslint": "^5.12.1",
		"vscode-test": "^1.0.2"
	}
}

```

```:src/extension.ts
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require("vscode");
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
function activate(context) {
    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    console.log('Congratulations, your extension "snippetconverter" is now active!');
    // The command has been defined in the package.json file
    // Now provide the implementation of the command with registerCommand
    // The commandId parameter must match the command field in package.json
    let disposable = vscode.commands.registerCommand('extension.toVSCode', () => {
        // The code you place here will be executed every time your command is executed
        let editor = vscode.window.activeTextEditor;
        if (editor) {
            let doc = editor.document;
            let cur_selection = editor.selection;
            if (editor.selection.isEmpty) {
                let startPos = new vscode.Position(0, 0);
                let endPos = new vscode.Position(doc.lineCount - 1, 10000);
                cur_selection = new vscode.Selection(startPos, endPos);
            }
            let text = doc.getText(cur_selection);
            let rows = text.split(/\r\n|\r|\n/);
            rows = rows.map((row) => {
                row = row.replace(/"/g, '\\"').replace(/(\$[^0-9{])/g, '\\\\$1');
                row = '"' + row + '",';
                return row;
            });
            text = rows.join('\n');
            editor.edit(edit => {
                edit.replace(cur_selection, text);
            });
            vscode.window.showInformationMessage('Strings is converted to VSCode Snippet.');
        }
    });
    context.subscriptions.push(disposable);
    let disposable2 = vscode.commands.registerCommand('extension.toSource', () => {
        // The code you place here will be executed every time your command is executed
        let editor = vscode.window.activeTextEditor;
        if (editor) {
            let doc = editor.document;
            let cur_selection = editor.selection;
            if (editor.selection.isEmpty) {
                let startPos = new vscode.Position(0, 0);
                let endPos = new vscode.Position(doc.lineCount - 1, 10000);
                cur_selection = new vscode.Selection(startPos, endPos);
            }
            let text = doc.getText(cur_selection);
            let rows = text.split(/\r\n|\r|\n/);
            rows = rows.map((row) => {
                row = row.slice(1).slice(0, -2)
                    .replace(/\\"/g, '"')
                    .replace(/(\\\\\$)/g, '$');
                return row;
            });
            text = rows.join('\n');
            editor.edit(edit => {
                edit.replace(cur_selection, text);
            });
            vscode.window.showInformationMessage('Strings is converted to source.');
        }
    });
    context.subscriptions.push(disposable2);
}
exports.activate = activate;
// this method is called when your extension is deactivated
function deactivate() { }
exports.deactivate = deactivate;
//# sourceMappingURL=extension.js.map
```

```:README.md
# snippetconverter README

## Features

You can convert source code to suitable fotmat for Visual Code Studio snippet file.

### 1.0.0

Initial release.



```


# パッケージファイルを生成

下記コマンドで、VSIX形式のファイルが生成されます。

```
vsce package
```


# VSCodeに拡張機能をインストール

あとはVSCodeにインストールするだけです。

VSCodeからコマンドパレットで「Extensions: Install from VSIX...」を実行します。
VSIXファイルを選択ダイアログが出てくるので、たった今作ったVSIXファイルを選択してインストールします。
これで拡張機能がインストールできました。
コマンドパレットで、自作のコマンド（ここでは「SnippetConverter: 〜」というコマンド）が検索できるようになります。