これは、農工大2025アドベントカレンダーの記事です。

はじめに

VS Code 拡張機能開発において、標準の UI だけでは実現できないリッチな表現が必要な場合に Webview API を使用した事例を紹介します。

Webview とは

VS Code 内に完全にカスタマイズ可能なビューを作成できる API です。 HTML、CSS、JavaScript を使用して、Web ブラウザのようなインターフェースをエディタ内に構築できます。

背景・動機

拡張機能を作っていると、「グラフを表示したい」「複雑なフォームを作りたい」「動画を埋め込みたい」といった、エディタ標準の TreeView や InputBox では対応しきれない要件が出てきます。 そんな時、自由度の高い Webview が唯一の解決策となることが多いです。

Webview 開発の「つらさ」

Webview は強力ですが、開発には独特の「つらさ」があります。

1. リソース読み込みの制限 (CSP & URI Scheme)

普段の Web 開発のように <script src="script.js"></script> と書いても動きません。 VS Code のセキュリティポリシー (Content Security Policy) に準拠する必要があり、かつローカルファイルへのパスは vscode-resource:/ のような特殊なスキーマに変換しなければなりません。

2. メッセージパッシングの複雑さ

Webview (HTML側) と Extension (Node.js側) は隔離された環境で動作するため、変数の直接共有ができません。 postMessage を介した非同期なメッセージのやり取りが必要となり、コードが複雑になりがちです。

実装のポイント

今回は、これらの課題を乗り越えてシンプルな Webview 拡張機能を作成する手順を解説します。

1. コマンド登録とパネルの作成

まずは Webview を表示するための「枠(パネル)」を作ります。 createWebviewPanel を使用しますが、ここで enableScripts: true を忘れると JS が動かないので注意です。

// extension.ts
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    let disposable = vscode.commands.registerCommand('vscodewebview.start', () => {
        
        // パネルの作成
        const panel = vscode.window.createWebviewPanel(
            'webviewDemo', // 内部的な識別子
            'Webview Demo', // タイトル
            vscode.ViewColumn.One,
            {
                enableScripts: true, // JSを有効化 (必須!)
                retainContextWhenHidden: true // 裏に回っても状態を維持
            }
        );

        // HTMLコンテンツを設定
        panel.webview.html = getWebviewContent();
        
        // ...後述のメッセージ処理
    });

    context.subscriptions.push(disposable);
}

2. HTML/CSS/JS の分離と読み込み

HTML を文字列でベタ書きするのは辛いので、外部ファイル化するのが定石です。 リソースパスの変換には asWebviewUri を使用します。これにより、Webview 内からローカルファイルへ安全にアクセスできるようになります。

import * as path from 'path';

// ディスク上のパスを取得
const scriptPathOnDisk = vscode.Uri.file(
    path.join(context.extensionPath, 'media', 'script.js')
);

// Webview用のURIに変換 (例: https://file+.vscode-resource.vscode-cdn.net/...)
const scriptUri = panel.webview.asWebviewUri(scriptPathOnDisk);

// HTML内で使用
// return `... <script src="${scriptUri}"></script> ...`;

3. 双方向通信 (Messaging)

Webview から Extension へ通知を送る実装です。 例えば、「ボタンが押されたら VS Code の通知を出す」といった連携が可能になります。

Webview 側 (送信): acquireVsCodeApi() は一度しか呼べないため、定数に保持して使います。

<script>
    const vscode = acquireVsCodeApi();
    function sendMessage() {
        vscode.postMessage({
            command: 'alert',
            text: 'Hello from Webview!'
        });
    }
</script>

Extension 側 (受信): onDidReceiveMessage でメッセージを待ち受けます。

        panel.webview.onDidReceiveMessage(
            message => {
                switch (message.command) {
                    case 'alert':
                        vscode.window.showInformationMessage(message.text);
                        return;
                }
            },
            undefined,
            context.subscriptions
        );

まとめ

Webview を使うと、VS Code の拡張機能開発の幅が一気に広がります。 リソース管理やメッセージングの仕組みさえ理解してしまえば、あとは通常の Web フロントエンド開発の知識がそのまま活かせます。

ぜひ、オリジナルのリッチな UI を持った拡張機能を作ってみてください!