とりゅふの森

GCPデータエンジニアとして生きる

【VSCode】拡張機能の開発を学ぶ(JavaScript Extension編)

f:id:true-fly:20210729221315p:plain

こんにちは、前回、前々回に引き続き、VSCodeの拡張機能開発についてまとめました。シンタックスハイライト、コードスニペットに続き、今回はJavaScriptでの拡張機能開発編です!
今まではノンプログラミングで実装してきましたが、今回からはJavaScriptで実際にロジックを実装するところまで検証しました。

プロジェクト作成

新規プロジェクトを作成します。

$ yo code

拡張機能の種類を選択する

? What type of extension do you want to create? 

今回は「New Extension (JavaScript)」を選択します。
JavaScript,TypeScriptが選択できますが、今回は筆者の都合上、JavaScriptで開発します。

拡張機能の名前、識別子、説明の設定

? What's the name of your extension? ()

作成する拡張機能の名前を入力します。今回は「truefly-ex」と入力してみます。

? What's the identifier of your extension? truefly-ex

拡張機能の識別子を決めます。デフォルトだと名前を識別子にしてくれるので、そのままEnterで進めます。

? What's the description of your extension?

拡張機能の説明を入力します。今回は空欄で、そのままEnterで進めます。

雛形を作成する

? Enable JavaScript type checking in 'jsconfig.json'? (y/N) 

yを選択します。

? Initialize a git repository? (Y/n)

Gitリポジトリを作成するか問われます。作っておいて損はないので「y」と入力してみます。

? Which package manager to use? (Use arrow keys)
> npm
  yarn

パッケージ管理ツールを選択します。今回はnpmを選択します。

以上、「truefly-ex」というディレクトリが作成され、中に拡張機能の雛形が作成されたので確認してみます。必要なパッケージのインストールされています。VSCodeで新規ウインドウを開き、作成されたフォルダを開いてみます。以下のように雛形となるファイル群が作成されていれば準備完了です!

f:id:true-fly:20210725110610p:plain

コマンド実行する拡張機能

package.jsonを見てみましょう。雛形で作ったプロジェクトでは拡張機能コマンドが定義されているのがわかります。"Hello World"というコマンドですね。

前略
    "activationEvents": [
        "onCommand:truefly-ex.helloWorld"
    ],
    "main": "./extension.js",
    "contributes": {
        "commands": [
            {
                "command": "truefly-ex.helloWorld",
                "title": "Hello World"
            }
        ]
    },
後略

実際のロジックは、extension.jsに記載されています。デフォルトで作成されたファイルの、コメント部分だけ抜粋しました。

const vscode = require('vscode');

function activate(context) {
    console.log('Congratulations, your extension "truefly-ex" is now active!');
    let disposable = vscode.commands.registerCommand('truefly-ex.helloWorld', function () {
        vscode.window.showInformationMessage('Hello World from truefly-ex!');
    });

    context.subscriptions.push(disposable);
}

function deactivate() {}

module.exports = {
    activate,
    deactivate
}

activate()deactivate()が、拡張機能がアクティブ、非アクティブ化されたときに呼び出される関数です。
vscode.commands.registerCommand()でコマンドを登録しています。
では、この状態でデバッグ実行してみましょう。
Ctrl+Shift+Pでコマンドパレットを開き、Hello Worldを実行します。

f:id:true-fly:20210725225848p:plain

画面右下にメッセージが出れば成功です。extension.jsvscode.window.showInformationMessage('Hello World from truefly-ex!');の箇所が無事実行されました。

f:id:true-fly:20210725230119p:plain

有効化している間常に機能する拡張機能

package.jsonでアクティブイベントを設定しています。ここを"*"に書き換えると、拡張機能が有効になった時に処理が走ります。

前略
    "activationEvents": [
        "*"
    ],
    "main": "./extension.js",
    "contributes": {
        "commands": [
            {
                "command": "truefly-ex.helloWorld",
                "title": "Hello World"
            }
        ]
    },
後略

合わせてextension.jsも編集してみます。今度はここで、SQLファイルに対して保管機能を実装しようと思います。

const vscode = require('vscode');

class SqlCompletionItemProvider {
    constructor (){
        this.completionItems = []

        // Reserved Keywords
        const keywords = ['ALL','AND','ANY','ARRAY','AS','ASC','ASSERT_ROWS_MODIFIED','AT','BETWEEN','BY','CASE','CAST','COLLATE','CONTAINS','CREATE','CROSS','CUBE','CURRENT','DEFAULT','DEFINE','DESC','DISTINCT','ELSE','END','ENUM','ESCAPE','EXCEPT','EXCLUDE','EXISTS','EXTRACT','FALSE','FETCH','FOLLOWING','FOR','FROM','FULL','GROUP','GROUPING','GROUPS','HASH','HAVING','IF','IGNORE','IN','INNER','INTERSECT','INTERVAL','INTO','IS','JOIN','LATERAL','LEFT','LIKE','LIMIT','LOOKUP','MERGE','NATURAL','NEW','NO','NOT','NULL','NULLS','OF','ON','OR','ORDER','OUTER','OVER','PARTITION','PRECEDING','PROTO','RANGE','RECURSIVE','RESPECT','RIGHT','ROLLUP','ROWS','SELECT','SET','SOME','STRUCT','TABLESAMPLE','THEN','TO','TREAT','TRUE','UNBOUNDED','UNION','UNNEST','USING','WHEN','WHERE','WINDOW','WITH','WITHIN','QUALIFY'];
        for (let i = 0; i < keywords.length; i++) {
            this.completionItems.push(
                {
                    label: keywords[i],
                    kind: vscode.CompletionItemKind.Keyword,
                    documentation: 'Visual BigQuery - Keywords'
                }
            )
        }

        // BigQuery Types
        const types = ['INT64','NUMERIC','BIGNUMERIC','FLOAT64','BOOL','STRING','BYTES','DATE','DATETIME','TIME','TIMESTAMP','ARRAY','STRUCT'];
        for (let i = 0; i < types.length; i++) {
            this.completionItems.push(
                {
                    label: types[i],
                    kind: vscode.CompletionItemKind.Field,
                    documentation: 'Visual BigQuery - Types'
                }
            )
        }

        this.completionList = new vscode.CompletionList(this.completionItems, false);
    }
    
    provideCompletionItems(document, position, token) {
        return Promise.resolve(this.completionList);
    }
}
function activate(context) {
    context.subscriptions.push(
        vscode.languages.registerCompletionItemProvider(
            { scheme: 'file', language: 'sql' },
            new SqlCompletionItemProvider(),
            '.'
        )
    );
}


function deactivate() {}

module.exports = {
    activate,
    deactivate
}

再度デバッグ実行して、拡張子が.sqlのファイルを開いてみます。以下の通り、拡張機能が有効化されている状態で、保管機能が動くのがわかります。

f:id:true-fly:20210725231916p:plain

f:id:true-fly:20210725231933p:plain

それぞれのアイコンは、kind: vscode.CompletionItemKind.Keywordkind: vscode.CompletionItemKind.Fieldで設定しています。

これをベースにextension.jsを編集すれば、構文チェッカだったり、フォーマッタだったりを実装することができそうです。

まとめ

以上、今回はVSCodeの拡張機能をextension.jsで作る方法についてご紹介しました。過去の記事のシンタックスハイライト、コードスニペットと違い、こちらでは実際にプログラムでVSCodeのAPIを実行して、より柔軟な処理を実装できます。今後は、BigQuerySQL専用の構文チェック、フォーマッタを作ったりと、自分のプロジェクトの環境に合わせた拡張機能を自作していきたいと思います。

徹底解説Visual Studio Code

徹底解説Visual Studio Code

Amazon