Gunosy Tech Blog

Gunosy Tech Blogは株式会社Gunosyのエンジニアが知見を共有する技術ブログです。

VS Code の拡張機能を作ったけどもっと便利なやつがあったどころか標準機能にあった話

こんにちは、ちょっと前に 3 ヶ月半の育児休業から復帰したくらさわです。 なんと今日で生まれてちょうど半年です!早い!!

こちらの記事は Gunosy Advent Calendar 2021 の 16 日目の記事です。 昨日の記事は yamaYu さんの『EKS Managed Node Group でカーネルパラメータを変更する 』でした。

今回は Visual Studio Code (以下 VS Code) の拡張機能を開発した話 + α を書きたいと思います。 タイトルが全てです。

はじめに

Prettier って便利ですよね。 私の VS Code の User/settings.json は以下の感じ (一部) で デフォルトフォーマッタを Prettier にし、他のフォーマッタを個別に指定してます。

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[go]": {
    "editor.defaultFormatter": "golang.go"
  },
  "[terraform]": {
    "editor.defaultFormatter": "hashicorp.terraform"
  }
}

最近よく書く yaml もフォーマットできるのでとてもいい感じです。 マークダウンまでフォーマットが効くのでこの記事も Prettier でフォーマットしてます。

そんな、たいていのファイルをフォーマットかけてる私ですが、たまにフォーマットをかけたくない時があります。 git の commit にフォーマットの差分を含めたくない時とかですね。

そういう時に、一時的に Prettier を無視してくれるツールがあったらいいなと、そんな思いで拡張機能を作成しました。 まあ作らなくてもあったんですが...

( 軽く探したんですが、探し方が甘かった。 というか、見つかってしまったら Advent Calendar のネタがなくなるという思いが我が眼を曇らせませた。 )

作成した拡張機能

Ignore Prettier という拡張機能を作成しました。

GitHub のリポジトリは こちら

機能としては

  1. .prettierignore に現在 VS Code で開いているファイルを追加する
  2. .prettierignore から現在 VS Code で開いているファイルを削除する

があり、1 と 2 の間で保存すれば一時的に Prettier のフォーマッタを無視できるというとても原始的なものです。

1 の時点で .prettierignore がなければ作成され, 2 の後に .prettierignore が空になれば削除するようにしています。

拡張機能作成の流れ

ここからは VS Code 拡張機能の作り方を軽く紹介しようと思います。 公式情報は Your First Extension にあります。

セットアップ

Yeoman と VS Code Extension Generator をインストール

npm install -g yo generator-code

Yeoman を実行

yo code

インタラクティブに質問されるのでそれに答えればプロジェクトが作成されます。
※ 私は Typescript を選択したのでこれ以下はその場合の説明だと思ってください。

プロジェクトを開く

やはり VS Code で開発を進めます。 VS Code でプロジェクトを開いて F5 を押すとデバッグ用の VS Code がまた別に立ち上がるのでそこで動かしながら開発していきます。

ちなみにプロジェクトを VS Code を開いた時に TypeScript + Webpack Problem Matchers がレコメンドされてるかもしれませんがそれがないとテストがうまく動きません。 それをインストールせずにいたらはまりました。

コード

私は今回コマンドを叩いたら処理が実行されるものを作成したのでそこを中心に説明します。 まず package.json に Extension Manifest の項目が追加されています。 コマンドを作成する時に修正する項目は contributes.commands, activationEvents の主に 2 つです。

contributes.commands

contributes.commands は公開するコマンドを以下のように定義します。

  "contributes": {
    "commands": [
      {
        "command": "helloworld.helloWorld",
        "category": "Hello",
        "title": "World"
      }
    ]
  },

これでコマンドパレットを開いた時に Hello: World とコマンドが表示され、helloworld.helloWorld に紐づいた処理が実行されます。 category はなくても大丈夫です。

こう表示される

activationEvents

activationEvents は拡張機能がアクティベートされるイベントを定義します。 以下のようにすると helloworld.helloWorld のコマンドが実行された時にアクティベートされます。

  "activationEvents": [
    "onCommand:helloworld.helloWorld"
  ],

activate 関数

拡張機能がアクティベートされた時に呼ばれる関数が src/extension.ts に定義されています。 はじめに定義されてるものはざっくり以下の感じです。

import * as vscode from "vscode";

export function activate(context: vscode.ExtensionContext) {
  let disposable = vscode.commands.registerCommand("helloworld.helloWorld", () => {
    vscode.window.showInformationMessage("Hello World from HelloWorld!");
  });

  context.subscriptions.push(disposable);
}

helloworld.helloWorld というコマンドに VS Code 内で Hello World from HelloWorld! と表示する処理を登録しています。 お察しのとおり、 package.json で contributes.commands に定義した command と紐づきます。

こう表示される

vscode には他もいろんな API が定義されています。 詳細は VS Code API にあります。

テスト

テストの公式情報は Testing Extensions にあります。 テスト用の VS Code を開いてテストする感じになります。Selenium とかでブラウザ使って自動テストする感じです。

package.json に test スクリプトが定義されていますが、それをそのまま実行してもテストできません。 いくつか方法があるみたいですが、サイドバーから RUN AND DEBUG を開いて Extension Tests を実行するとテストできます。

ここから実行する

ちなみに、.vscode/launch.json に実行される処理の定義があります。 以下一部

{
  "configurations": [
    {
      "name": "Extension Tests",
      "type": "extensionHost",
      "request": "launch",
      "args": [
        "--extensionDevelopmentPath=${workspaceFolder}",
        "--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
      ],
      "outFiles": [
        "${workspaceFolder}/out/**/*.js",
        "${workspaceFolder}/dist/**/*.js"
      ],
      "preLaunchTask": "tasks: watch-tests"
    }
  ]
}

テストの前処理が preLaunchTask に見慣れない形式で task が定義されていますが、これは .vscode/tasks.json に定義されていました。 VS Code の機能みたいです。 参考: https://code.visualstudio.com/docs/editor/tasks

この task を実行する時に前述した TypeScript + Webpack Problem Matchers が必要でした。 インストールしていなかったので、テストが実行できずはまりました。

拡張機能を公開

公式情報は Publishing Extensions にあります。

Azure DevOps のアカウント作成 & Token 発行

公開するには Azure DevOps のアカウントが必要なのでない場合は作成します。 アカウントを作成したら Personal Access Token を発行します。

Publishing Extensions にスクショとかあります。

vsce をインストール

公開には Visual Studio Code Extensions 略して vsce というパッケージを使用します。

npm install -g vsce

publisher を作成

公開するため Visual Studio Marketplace publisher management page から publisher を作成します。 ここの Name が、拡張機能のページで表示されます。

ここに表示される

そして ID は拡張機能の Identifier になります。 settings.json などで使用する esbenp.prettier-vscodeesbenpID です。

また publisher は package.json に追加する必要があるので追加します。 ID を追加します。

publisher を作成したら以下のコマンドでログインできたら成功です。 この時 Personal Access Token を使用します。

vsce login <publisher id>

publish

以下コマンドで公開できます。

vsce publish

もし package.json に足りない項目があったり何か不備があるとエラーになるので修正しましょう。

私は license や repository が package.json に足りませんでした。 あと README をちゃんと書いてなくてもエラーになりました。

成功したら Visual Studio Marketplace publisher management page で確認できます。

お疲れさまでした。

さいごに

今のままだと、おそらく自分の作ったものを使うことはなさそうですが、拡張機能作成を通して色々勉強になったのでやってよかったなと感じています。 これからもこれに懲りずに何か思いついたら作っていきたいです。

ということでお待たせしました。 私の拡張機能より便利なものはこちらです。

Formatting Toggle

インストールすると右下にフォーマットをかける、かけないをトグルできる拡張機能が表示されます。結構有名そうなので知ってる方も多いかもしれません。

ここに出てくる

そして標準機能は Save Without Formatting って機能です。

Command + Shift + P 押して save without formatting って打てば見つかります。 一回フォーマットせずに保存したいだけならこれが一番手軽かもです。

こうやって使える

ありがとうございました。