はじめに
賢すぎる愛犬にいっそコードレビューを頼みたいと思ったことはありませんか?私はあります。
この記事では、Gemini APIを使用して愛犬がコードレビューしてくれるVSCode拡張機能を作成します。
動作イメージ
- VSCodeのサイドバーに愛犬の画像とテキストエリアを表示
- コードを選択してコマンドを実行すると、犬っぽい口調でレビューを返してくれる
- レビュー中やレビュー完了時は専用の画像に切り替わる演出付き
プロジェクトの作成
公式ドキュメントを参考に、Yeomanでプロジェクトを作成します。
npm install --global yo 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? dog-reviewer
? What's the identifier of your extension? dog-reviewer
? What's the description of your extension?
? Initialize a git repository? No
? Which bundler to use? webpack
? Which package manager to use? npm
これでmy-pets-reviewer
フォルダが作成され、拡張機能のプロジェクトが出来上がりました。
今回はGoogle AI Gemini APIを使用するため、APIキーとライブラリも用意します。
npm install @google/generative-ai
WebViewとコマンドの登録
拡張機能の起動時には、src/extension.ts
内のactivate
関数が呼び出されます。
activate
関数に以下のコードを記述します。
export function activate(context: vscode.ExtensionContext): void {
const provider = new WebViewProvider(context);
// サイドバーのWebView登録
context.subscriptions.push(
vscode.window.registerWebviewViewProvider("dogReviewerSidebar", provider)
);
// コードレビューコマンドの登録
const codeReviewCommand = vscode.commands.registerCommand(
"extension.codeReview",
async () => {
await handleCodeReview(provider);
}
);
context.subscriptions.push(codeReviewCommand);
}
VSCodeのサイドバーにWebView(HTML)を表示するため、WebViewProvider
インスタンスを作成し、vscode.window.registerWebviewViewProvider
関数を使ってサイドバー(dogReviewerSidebarというIDのビュー)へ登録しています。
vscode.commands.registerCommand
関数では、コマンドextension.codeReview
を登録しています。これにより、エディタ上で選択したテキストに対して、ショートカットメニューから「コードレビュー」コマンドを実行できるようになります。
続いて、拡張機能の設定をpackage.json
に追記します。
"contributes": {
"views": {
"explorer": [
{
"type": "webview",
"id": "dogReviewerSidebar",
"name": "DogReviewer"
}
]
},
"commands": [
{
"command": "extension.codeReview",
"title": "コードレビュー"
}
],
"menus": {
"editor/context": [
{
"command": "extension.codeReview",
"when": "editorHasSelection",
"group": "navigation"
}
]
},
"configuration": {
"type": "object",
"title": "Dog Reviewer",
"properties": {
"dogReviewer.apiKey": {
"type": "string",
"default": "",
"description": "Gemini API Key"
}
}
}
},
contributes
では、WebViewやコマンドの識別子を定義します。views
でサイドバー(エクスプローラー)にWebViewを追加し、commands
で「コードレビュー」コマンドを登録、menus
でメニューにコマンドを表示する設定をしています。
また、ユーザー設定からGemini APIキーを取得できるように、configuration
で設定項目を定義しています。
コマンド実行時の処理
extension.codeReview
コマンドが呼ばれた際に実行される処理を実装します。
async function handleCodeReview(provider: WebViewProvider): Promise<void> {
const editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showInformationMessage("アクティブなエディタがありません。");
return;
}
// 設定から API キーを取得
const config = vscode.workspace.getConfiguration("dogReviewer");
const apiKey = config.get<string>("apiKey");
if (!apiKey) {
vscode.window.showErrorMessage("APIキーが設定されていません。拡張機能の設定からAPIキーを入力してください。");
return;
}
const selection = editor.selection;
const selectedText = editor.document.getText(selection);
// プログレスインジケータの表示中にレビューを実行
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: "コードをレビュー中...",
cancellable: false,
},
async () => {
provider.setImage("Progress.png");
const genAI = new GoogleGenerativeAI(apiKey);
const prompt = `あなたは賢いボーダーコリーです。以下のコードの問題点を犬の口調で簡潔にレビューしてください。\n\n${selectedText}`;
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
// プロンプトを送信して結果を取得
const result = await model.generateContent(prompt);
const response = result.response;
const text = response.text();
// 結果をWebViewに表示
provider.setReviewText(text);
provider.setImage("Success.png");
}
);
}
アクティブなエディタから選択中のテキストを取得し、withProgress
で進捗インジケータを表示しながら処理を行っています。
犬にコードレビューしてもらう必要があるので、prompt
として「犬の口調で簡潔にレビューしてください」という指示とともにコードを渡します。
WebViewの表示
class WebViewProvider implements vscode.WebviewViewProvider {
private _view?: vscode.WebviewView;
private _reviewText: string = "";
private _currentImage: string = "Wendy.jpg";
constructor(private readonly context: vscode.ExtensionContext) {}
// WebViewの初期化処理
resolveWebviewView(webviewView: vscode.WebviewView) {
this._view = webviewView;
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [this.context.extensionUri],
};
this.updateWebviewContent();
}
// WebViewを更新する
private updateWebviewContent() {
if (!this._view) {
return;
}
// _currentImageで指定した画像ファイルへのURIを取得
const imageUri = this._view.webview.asWebviewUri(
vscode.Uri.joinPath(
this.context.extensionUri,
"media",
this._currentImage
)
);
// CSSファイルへのURIを取得
const cssUri = this._view.webview.asWebviewUri(
vscode.Uri.joinPath(this.context.extensionUri, "media", "style.css")
);
// WebViewに表示するHTML
this._view.webview.html = `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="${cssUri}">
</head>
<body>
<div class="image-container">
<img src="${imageUri}" style="max-width: 100%; height: auto;">
</div>
<div class="review-container">${this._reviewText}</div>
</body>
</html>`;
}
// Geminiから取得したコードレビューをWebViewに反映する
public setReviewText(text: string) {
this._reviewText = text;
this.updateWebviewContent();
}
// 表示画像を更新する
public setImage(imageName: string) {
this._currentImage = imageName;
this.updateWebviewContent();
}
}
WebviewViewProvider
を利用して、サイドバーに愛犬の画像とレビュー結果を表示します。
_reviewText
や_currentImage
の変更に合わせてupdateWebviewContent
を呼び出すことで、WebViewを更新しています。
注意点として、asWebviewUri()
を使わないと、ディレクトリ内の画像やCSSファイルに直接アクセスできません。
動作確認
F5
を押して拡張機能をデバッグ実行します。
新しいVSCodeウィンドウが立ち上がり、サイドバーに愛犬の画像が表示されました。
次に、拡張機能の設定でGemini APIキーを入力します。
適当なファイルを開き、コードを選択した状態でショートカットメニューから「コードレビュー」を実行します。
数秒後、レビュー文が返ってきました。
ちゃんと犬っぽい口調でコードの解説と問題点の指摘をしてくれています。
まとめ
以上で完成です。お疲れ様でした!🎉
VSCodeの拡張機能は公式のドキュメントやサンプルがとても充実しているので、比較的簡単に作ることができます。
皆さんもぜひ作ってみてください!