【2023年】これからブログを始める人へおすすめの書籍

Kissy

【入門編】Visual Studio Code拡張機能の作り方

作成: 更新:

Visual Studio Code拡張機能の作り方

Visual Studio Codeを使っていて、こんな機能があったら便利だなと思うことが良くあります。Visual Studio Codeには拡張機能があり、追加で必要な機能を拡張できるため、たいていの機能は公開されている拡張機能を探せば見つかります。

ただ、とてもニッチ機能など見つからない場合があります。誰かが作ってくれるのを待つのもいいですが、あまりにニッチだと永遠に待つことになってしまいます。

実は、Visual Studio Codeの拡張機能は自分で作成することができます。作成した拡張機能はMarketplaceで公開することもできます。そう。なければ作ってしまえばいいんです。

ということで、ここでは、自分でVisual Studio Codeの拡張機能を作成する方法を記載しています。拡張機能を作るにあたって必要な開発環境のインストールから、簡単な拡張機能を作るまでを紹介します。

Marketplaceへの登録については、また別の機会に紹介したいと思います。

開発環境

まずは開発環境を準備していきます。

環境

  • Windows 10
  • Visual Studio Code
  • Node.js
  • Yeoman
  • generator-code

インストール

  1. Visual Studio Codeをインストールします。
     ここからダウンロードし、インストーラーに従ってインストールします。開発するソフトウェアによっては、拡張機能を追加でインストールすることになると思いますが、ここでは省略します。

  2. インストールを確認します。
     コマンドプロンプトから以下を実行します。バージョンが表示されれば、インストールできています。

    > code -v
    1.39.2
    6ab598523be7a800d7f3eb4d92d7ab9a66069390
    x64
    
  3. Node.jsをインストールします。
     ここからダウンロードしてインストールします。インストーラーに従ってインストールすればオッケーです。

  4. インストールを確認する
     コマンドプロンプトから以下を実行します。バージョンが表示されれば、インストールできています。

    > node -v
    v11.13.0
    
  5. yoをインストールします。
     コマンドプロンプトから以下を実行します。

    > npm install -g yo
    
  6. generator-codeをインストールします。
     コマンドプロンプトから以下を実行します。

    > npm install -g yo generator-code
    
  7. node.jsのパスを確認します。
     コマンドプロンプトから以下を実行します。するとパスが表示されるので、これをコピーしておきます。

    > npm bin -g
    C:\Users\kissy\AppData\Roaming\npm
    
  8. 環境変数NODE_PATHを設定します。
     [システムのプロパティ]-[詳細設定]-[環境変数]を開き、[システム環境変数]に環境変数NODE_PATHを追加し、先ほどコピーしたパスを変数値として設定します。

  9. これで開発環境の構築は完了です。

HelloWorldの作成と実行

まずは、簡単な拡張機能を作成して実行してみます。ここでは、generator-codeで生成できるひな型のHelloWorldのコード生成~実行までを行います。

ひな型コードを生成する

まずは、ひな型となるコードを生成します。

  1. コマンドプロンプトを起動します。

  2. ソースコードを保存するディレクトリまで移動します。

  3. generator-codeでひな型コードを生成します。
    まず、以下のコマンドを実行します。

    yo code
    

    すると、ひな型コードを生成するために必要な情報を聞かれるので、入力していきます。

    	     _-----_     ╭──────────────────────────╮
        |       |    │   Welcome to the Visual  │
        |--(o)--|    │   Studio Code Extension  │
       `---------´   │        generator!        │
        ( _´U`_ )    ╰──────────────────────────╯
        /___A___\   /
         |  ~  |
       __'.___.'__
     ´   `  |° ´ Y `
    
    ? What type of extension do you want to create? New Extension (TypeScript)
    ? What's the name of your extension? HelloWorld
    ? What's the identifier of your extension? helloworld
    ? What's the description of your extension?
    ? Initialize a git repository? No
    ? Which package manager to use? npm
    
  4. これでひな型コードの生成は終わりです。

拡張機能をデバッグ実行する

以下の手順で拡張機能をデバッグ実行できます。あくまでデバッグ実行なので、通常のVSCにインストールはされません。

  1. 以下のコマンドを実行し、作成されたひな型コードをVisual Studio Codeで開きます。

    cd helloworld
    code .
    
  2. F5を押してビルド+実行をします。
     ただし、この段階では拡張機能は実行されません。拡張機能を実行する新たなVisual Studio Codeのウィンドウが立ち上がるだけです。

  3. Control+Shift+Pを押しコマンドパレットを開きます。
     コマンドパレットとは、メニューバーの下に現れるコマンドを入力するための小さなウィンドウのことです。

  4. コマンドパレットでHello Worldと入力します。

  5. 拡張機能が起動され、右下にHello World!のメッセージが表示されます。

サンプルソースの解説

ファイル構成

Yeomanが作成するひな型のソースコード構成は以下の通りです。Visual Studio Code拡張機能の開発で、主に修正するのは「package.json」と「extension.ts」のファイルになります。

ファイル 内容
CHANGELOG.md サンプルの変更履歴
package-lock.json 依存ライブラリのバージョンを固定する
package.json 拡張機能の名前/説明/バージョン/エントリーポイント、依存ライブラリを記載する
README.md サンプルのreadme
tsconfig.json TypeScriptのコンパイル対象やコンパイルオプションを記載する
tslint.json TSLint(TypeScriptの静的テストツール)のルールを記載する
/src/extension.ts 拡張機能のメインのソースコード
/src/test/ 拡張機能のテストコード

package.json

「package.json」は拡張機能の名前/説明/バージョン/エントリーポイント、依存ライブラリを記載するJSONファイルです。拡張機能に機能追加する際、まず、この「package.json」に機能のイベントや定義を記載していきます。

各項目の概要は以下の通りです。

項目 必須 タイプ 概要
name string 拡張機能の名称(すべて小文字。空白NG。)
version string SemVer形式のバージョン
publisher string 作成者名
engines object 拡張機能と互換性のあるVSCodeのバージョンを記載
categories string 拡張機能のカテゴリ(取りえる値:Programming Languages, Snippets, Linters, Themes, Debuggers, Formatters, Keymaps, SCM Providers, Other, Extension Packs, Language Packs, Data Science, Machine Learning, Visualization, Notebooks)
activationEvents array 拡張機能を有効にするイベント
main string 拡張機能のエントリーポイント
contributes object 拡張機能のコントリビュート(機能)の定義

例えば、HelloWorldではactivationEventscontributesは以下のように記載されていると思います。

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

helloworld.helloWorldコマンドで拡張機能が有効になるよ、helloworld.helloWorldコマンドのタイトルはHello Worldだよ、ということを定義しています。

helloworld.helloWorldコマンドを実行したときの動作は、後述するextension.tsで定義していきます。

その他の項目や詳細については下記のサイトを参照ください。

extension.ts

extension.tsは拡張機能の機能を実装していくファイルになります。拡張子がtsとなっているように、TypeScriptで実装します。

以下は、HelloWorldのextension.tsのコードです。拡張機能の実装としては、activate()deactivate()の2つの関数が実装されています。

activate()package.jsonactivationEventで定義したイベントが発生した際に呼ばれる関数です。deactivate()は拡張機能が終了する際に呼ばれ、後処理をするタイミングとして使えます。

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
  console.log('Congratulations, your extension "helloworld-sample" is now active!');

  let disposable = vscode.commands.registerCommand('helloworld.helloWorld', () => {
    vscode.window.showInformationMessage('Hello World!');
  });

  context.subscriptions.push(disposable);
}

export function deactivate() {}

HelloWorldでは、helloworld.helloWorldコマンドを実行すると「Hello World!」というメッセージを表示します。メッセージを表示するためには、VSCode APIのshowInformationMessage()を使います。また、コマンドとして使えるように(コマンドパレットに表示されるように)するために、VSCode APIのregisterCommand()でコマンド登録しています。

registerCommand()は戻り値としてdisposableを返します。disposableはコマンドの後処理を行ってくれる関数で、context.subscriptions.push(disposable)としておくことで、拡張機能が無効になるタイミングでコマンドの後処理を行ってくれます。

エラー発生時の対処法

Visual Studio Code拡張機能を作る際にいくつかハマったので備忘録として記載しておきます。

generator-codeのインストールでエラー

generator-codeをインストールしたところ、インストール後に以下のエラーが発生しました。

npm install -g yo generator-code
...
x NODE_PATH matches the npm root

環境変数NODE_PATHを見ても、パスは設定されているので、無視してyo codeを実行してみたところ、今度は以下のようなエラーが発生しました。

D:\Users\kissy\Documents\Visual Studio Code Extension\HelloWorld>yo code

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

? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? HelloWorld
? What's the identifier of your extension? helloworld
? What's the description of your extension?
? Initialize a git repository? No
? Which package manager to use? npm
events.js:187
      throw er; // Unhandled 'error' event
      ^

AssertionError [ERR_ASSERTION]: Trying to copy from a source that does not exist: C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\generators\app\templates\ext-command-ts/vscode
    at EditionInterface.exports.copy (C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\node_modules\mem-fs-editor\lib\actions\copy.js:48:3)
    at module.exports._writingCommandTs (C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\generators\app\index.js:666:17)
    at module.exports.writing (C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\generators\app\index.js:539:22)
    at Object.<anonymous> (C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\node_modules\yeoman-generator\lib\index.js:424:27)
    at C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\node_modules\run-async\index.js:25:25
    at new Promise (<anonymous>)
    at C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\node_modules\run-async\index.js:24:19
    at C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\node_modules\yeoman-generator\lib\index.js:425:13
    at processImmediate (internal/timers.js:439:21)
Emitted 'error' event on Generator instance at:
    at Immediate.<anonymous> (C:\Program Files (x86)\Nodist\bin\node_modules\generator-code\node_modules\yeoman-generator\lib\index.js:433:22)
    at processImmediate (internal/timers.js:439:21) {
  generatedMessage: false,
  code: 'ERR_ASSERTION',
  actual: false,
  expected: true,
  operator: '=='
}

仕方ないので、はじめからやってみようと思い、Node.jsをアンインストール(ついでにNodistもアンインストール)、それからNode.jsを再度インストールからしてみました。それでもやっぱり「npm install -g yo generator-code」をしたところで、「NODE_PATH matches the npm root」が発生してしまいました。。

途方に暮れてもう一度、環境変数NODE_PATHを見てみると、なぜかNODE_PATH自体がありませんでした。

そこで、環境変数NODE_PATHを設定したところ、無事「npm install -g yo generator-code」も「yo code」も成功しました。

通常はNode.jsのインストールで設定されるはずですが、追加されていませんでした。もしかすると、Nodistが悪さをしていたのかもしれません。

とりあえず動いたのでそれ以上は深追いしなかったため、本当の原因は分かっていません。

ビルドが永遠に終わらない

generator-codeのエラーが解決して、ようやく拡張機能開発のひな型を作成できるようになりました。

早速ビルドして実行使用してみたのですが、今度は「ビルド中…」と出たままで、いつまでもビルドが完了しない現象が発生。。

この現象、いろいろとググっても情報が見つからず、またまた途方に暮れることになりました。

ひな型コードを再作成してみたり、PC再起動やyo、generator-codeの再インストールなどもやっては見たものの治らず。結局その後、Visual Studio Codeを再インストールしたら、あっさりとビルドでき実行できました。

うーん。変なところでつまづくことが多いなぁ、と思いながらも、とりあえず動いたから良し。

参考サイト

最後まで読んでいただきありがとうございます。
また読んでくださいませ。
そんじゃーね。

関連記事

SPONSORED LINK
SPONSORED LINK