[vscode] 競技プログラミングのライブラリを簡単にコピペする拡張機能を作った

<img height="280" alt="logo" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223255/f091ce3d-9e1a-a12b-78e7-19521f2700d3.png">

# TL;DR

vscodeでライブラリを簡単にコピペするための拡張機能を作りました。

- [InsertTemplate - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=yskoht.vscode-insert-template)

# Demo

Command Palette (`Cmd + Shift + P`)で`Insert Template`を選択（または、ショートカット`Ctrl + Shift + I`）で特定のディレクトリ以下のファイルを選択できます。選択されたファイルがアクティブなファイルに挿入されます。

![example.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223255/5e62ce00-ebfa-1cdc-d028-c4915ee9a49e.gif)

# 設定

テンプレートファイルを検索するディレクトリはデフォルトで`~/.vscode-templates`になっていますが、`settings.json`に`insertTemplate.directory`を設定することで変更できます。挿入するファイルの候補としたくないファイルは`insertTemplate.ignore`にglobパターンの配列を指定できます。

```json:settings.json
"insertTemplate.directory": "~/prg/competitive-programming",
"insertTemplate.ignore": ["**/*.md", "**/tmp/**"],
```

# 背景

最近またatcoderをやっています。`vscode 競技プログラミング`等で調べるとライブラリのコピペはスニペットを使う方法が紹介されていますが、JSONはメンテナンスが難しいなと思っていました。

やりたいことは、特定のディレクトリにライブラリを記述したファイルを配置しておいて、ファイル単位でそのままアクティブなファイルに貼り付けるということです。ライブラリはそのままgitで管理できる形が良いなと思いました。

vimキーバインドであれば`:r path/to/file`で達成できますが、できるだけ簡単に、インクリメンタルサーチとか使える感じで解決したかったので、車輪の再発明っぽいですが、拡張機能を作ることにしました。

# vscode拡張機能の作り方

環境

- macOS 10.14.6
- Visual Studio Code 1.43.2

ソースコードは以下です。

- [yskoht/vscode-insert-template: Insert template file extension for vscode](https://github.com/yskoht/vscode-insert-template)

## helloworldする

- [Your First Extension | Visual Studio Code Extension API](https://code.visualstudio.com/api/get-started/your-first-extension)

メニューバーから`[Run]->[Start Debugging]`でデバッグ実行ができ、DEBUG CONSOLEに`console.log`が出力されることが重要。

## APIをさがす

- [VS Code API | Visual Studio Code Extension API](https://code.visualstudio.com/api/references/vscode-api)

今回は、以下のAPIを使いました。

- `vscode.workspace.getConfiguration`: `settings.json`から設定を取得
- `vscode.window.showQuickPick`: ユーザーが選択候補の中から１つの候補を選択（テンプレートファイルを選択する際に使用）
- `vscode.workspace.openTextDocument`: 新しいファイルを作成、既存ファイルを開く（アクティブなファイルがない場合は新しいファイルを作成する、またテンプレートファイルを開く際に使用）
- `vscode.window.showTextDocument`: ドキュメントを表示（新しく作ったファイルを表示する際に使用）
- `vscode.window.activeTextEditor`: アクティブなテキストエディタを取得
- `vscode.window.showInformationMessage`: 警告メッセージを表示
- `vscode.window.showErrorMessage`: エラーメッセージを表示

## package.jsonを整理する

- [Extension Manifest | Visual Studio Code Extension API](https://code.visualstudio.com/api/references/extension-manifest)
- [Contribution Points | Visual Studio Code Extension API](https://code.visualstudio.com/api/references/contribution-points)

必須ではないですが見栄えが良くなるので、以下のプロパティを追加、修正。

- `author.name`: 追加
- `icon`: 追加。最低128x128
- `galleryBanner`
    - `color`: マーケットプレイスのヘッダーの背景色
    - `theme`: ヘッダーの文字色。`light`だと黒。`dark`だと白
    <div><img width="640" alt="banner" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223255/e8b34bc5-a685-0a00-2c7a-3a622171a729.png"></div>
- `contributes`: 書いておくと拡張機能の`Feature Contributions`に表示される
    - `commands.title`: 拡張機能のコマンド名
    - `keybindings`: 拡張機能のコマンドをキーバインドにアサイン
    - `menus.editor/context`: エディターの右クリックメニューにコマンドを追加
    - `configuration`: `settings.json`で設定できるプロパティ
    <div><img width="640" alt="contributions" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223255/8aea0313-fcd4-bce9-c564-b8c9a5a0a675.png"></div>
- `license`: 追加
- `bugs`: 追加
- `repository`: 追加
- `homepage`: 追加

## README.mdにバッジをつける

- [VSMarketplaceBadge](https://vsmarketplacebadge.apphb.com/)
    - バッジはかっこいいのでつけます。マーケットプレイスに登録してからのほうがわかりやすいかもしれません

## CHANGELOG.mdを整理する

- [Keep a Changelog](https://keepachangelog.com/ja/1.0.0/)
    - ベストエフォートで書きます
    
## マーケットプレイスに登録する

- [Publishing Extensions | Visual Studio Code Extension API](https://code.visualstudio.com/api/working-with-extensions/publishing-extension)
    - `vsce create-publisher`すると`Personal Access Token (PAT)`を入力を求められます

# ハマった点

![error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223255/c272128a-c5b4-4c56-dc1e-bef3c9b36c87.png)

デバッグ実行では問題ないのに、マーケットからインストールすると`command 'extension.insertTemplate' not found`で動かない。

## ローカルでパッケージからインストール

いちいちマーケットにアップロードしてデバッグするのは辛いので、ローカルでプロダクションバージョンをインストールする。

- [Install from a VSIX - Managing Extensions in Visual Studio Code](https://code.visualstudio.com/docs/editor/extension-gallery#_install-from-a-vsix)

```bash
vsce package # vscode-insert-template-x.x.x.vsixが生成される
code --install-extension vscode-insert-template-x.x.x.vsix
```

vscodeは拡張機能を自動でアップデートしてしまうので、デバッグ中は無効にしておくと良いです。

```json:settings.json
"extensions.autoUpdate": false,
```

## Toggle Developer Tools

メニューバーから`[Help]->[Toggle Developer Tools]`を選択するとChromeのデベロッパーツールが表示されるので`Console`でログを確認する。

```
abstractExtensionService.ts:396 Activating extension 'yskoht.vscode-insert-template' failed: Cannot find module 'glob'
Require stack:
- /Users/yskoht/.vscode/extensions/yskoht.vscode-insert-template-0.0.6/out/extension.js
- /Applications/Visual Studio Code.app/Contents/Resources/app/out/vs/loader.js
- /Applications/Visual Studio Code.app/Contents/Resources/app/out/bootstrap-amd.js
- /Applications/Visual Studio Code.app/Contents/Resources/app/out/bootstrap-fork.js.
```

`glob`が見つかってない🤮

## `devDependencies` -> `dependencies`

`glob`のインストール先が`devDependencies`になっていたので`dependencies`に変更。無事に動作するようになりました。

```diff
diff --git a/package.json b/package.json
index b2b480e..d229d9a 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,9 @@
                "pretest": "npm run compile && npm run lint",
                "test": "node ./out/test/runTest.js"
        },
+       "dependencies": {
+               "glob": "^7.1.6"
+       },
        "devDependencies": {
                "@types/glob": "^7.1.1",
                "@types/mocha": "^7.0.1",
@@ -78,7 +81,6 @@
                "@typescript-eslint/eslint-plugin": "^2.18.0",
                "@typescript-eslint/parser": "^2.18.0",
                "eslint": "^6.8.0",
-               "glob": "^7.1.6",
                "mocha": "^7.0.1",
                "typescript": "^3.7.5",
                "vscode-test": "^1.3.0"
```

## Your extension folder

- [Where are extensions installed? - Managing Extensions in Visual Studio Code](https://code.visualstudio.com/docs/editor/extension-gallery#_where-are-extensions-installed)

macOSだと`~/.vscode/extensions`に拡張機能が保存される。古いバージョンが溜まっていくことがあるので手動で消してもよいかもしれない。

# まとめ

- タイトルに競技プログラミングと付けたけど、あまり関係なかったかもしれない。でも他のファイルをコピペしたいというユースケースは競プロぐらいしかないような気もする
    - もっとスマートな解決方法があれば教えて下さい。とても車輪の再発明感を感じます
- vscodeの拡張機能は簡単に作れてとてもよいです。ロゴ作ったり、README書いたり、CHANGELOG書いたりするのが一番たいへん

# 参考

- [雑なVSCode拡張を作ろう #kyotoasterisk - hitode909の日記](https://blog.sushi.money/entry/2018/08/18/163959)
- [Visual Studio Code の拡張をはじめて作ったときの流れ - てくなべ (tekunabe)](https://tekunabe.hatenablog.jp/entry/2019/01/12/vscode-l2mdtable)
- [Visual Studio Code の拡張機能を開発してみよう](https://ryuta46.com/1190)
- [Visual Studio CodeがRSSリーダになる？VS Codeの拡張機能を作れば作業が捗る | Raccoon Tech Blog [株式会社ラクーンホールディングス 技術戦略部ブログ]](https://techblog.raccoon.ne.jp/archives/53610805.html)