VSCode拡張機能「Unicode Hover Preview」を作ってみた
こんにちは、TRUSTART株式会社の片ノ坂です!今回、最新のLLM(大規模言語モデル)サービスであるClaude Codeを活用してVSCode拡張機能を開発した経験についてご紹介します!
1. 「Unicode Hover Preview」拡張機能とは?
今回開発した「Unicode Hover Preview」は、VSCode上でUnicode文字(例:\u3042
など)にカーソルをホバーすると、指定したフォントで見たときの見た目など詳細情報をツールチップで表示する拡張機能です。
実装中やレビュー中にUnicode文字を出てきた際、「この文字って何だっけ?」といった疑問が生まれがちじゃないでしょうか(私はよくあります)。このような疑問をエディタから離れることなく解消することができます。開発中のちょっとしたストレスを軽減し、生産性向上に寄与すると考えて実装しました。
2. VSCode拡張機能開発の基礎
2.1 プロジェクトの作成
VSCode拡張機能の開発には、Yeomanのジェネレータ generator-code
を利用するのが一般的です。以下のコマンドで簡単にプロジェクトのひな形を作成できます。質問に答えていくだけで開発環境が整います。
npm install -g yo generator-code
yo code
ですが、今回はClaude Codeに作ってもらったのでこの工程は踏んでいません。悪しからず。
Claude Codeへの指示内容は以下です。ちなみに、会話の履歴は ~/.claude/projects/
配下にjsonlで保存されてました。
VSCodeのプラグインを作成したい。具体的にはUnicode(例:u\\abcdなど)の文字列にマウスオーバーすると、指定したフォントの対応する文字を表示してくれるプラグインを作りたい。
package.json
の設定
2.2 VSCodeのプラグインの各種設定に関してはpackage.jsonが利用されます。こちらのサイトを見ることで項目とその内容を把握することができます。
余談ですが、npmパッケージも同様にpackage.jsonを使いまして、それはこちらのサイトを見ることで項目とその内容を把握することができます。
package.jsonの項目の中で今回はactivationEventsとcontributionを特に使っています。
-
https://code.visualstudio.com/api/references/activation-events
- プラグインがアクティベート(起動)される条件の設定を行います
- 値はJSON形式ではないのが特徴
- 今回の実装ではプラグインが動作する言語の設定(onLanguage)をしました
{ // 中略 "activationEvents": ["onLanguage:*"], // 中略 }
- https://github.com/trustart-co-jp/unicode-hover-preview/blob/ddac78c9f065a9faf072ac734a94ef81fa538548/package.json#L17C1-L17C40
-
https://code.visualstudio.com/api/references/contribution-points
- プラグインがVSCodeと統合して動作するにあたっての設定をするようです
- こちらは値がJSON形式
- 今回の実装では以下を行なっています
- プラグインの設定をVSCodeの設定画面でできるようにするための定義
- コマンドパレットから呼び出して実行する際の定義
{ // 中略 "contributes": { "commands": [ { "command": "unicodeHoverPreview.setFont", "title": "Set Unicode Preview Font" } ], "configuration": { "title": "Unicode Hover Preview", "properties": { "unicodeHoverPreview.fontFamily": { "type": "string", "default": "Arial Unicode MS", "description": "Font family for Unicode character preview" }, "unicodeHoverPreview.fontSize": { "type": "number", "default": 24, "description": "Font size for Unicode character preview" }, "unicodeHoverPreview.enabled": { "type": "boolean", "default": true, "description": "Enable/disable Unicode hover preview" } } } }, // 中略 }
- https://github.com/trustart-co-jp/unicode-hover-preview/blob/ddac78c9f065a9faf072ac734a94ef81fa538548/package.json#L19-L47
2.3 Hover Providerの実装
VSCode拡張機能のメインとなるのが src/extension.ts
ファイルです。ここでVSCode APIを使って機能を実装しています。
まず、拡張のアクティベート、ディアクティベート時の処理を作成します。
/**
* 拡張機能をアクティベートします
* @param context - 拡張機能のコンテキスト
*/
export function activate(context: vscode.ExtensionContext) {
const provider = new UnicodeHoverProvider()
// 全ての言語に対してホバープロバイダーを登録
const hoverDisposable = vscode.languages.registerHoverProvider({ scheme: "file" }, provider)
// フォント設定用のコマンドを登録
const commandDisposable = vscode.commands.registerCommand("unicodeHoverPreview.setFont", () => {
provider.showFontPicker()
})
context.subscriptions.push(hoverDisposable, commandDisposable)
}
/**
* 拡張機能を非アクティベートします
*/
export function deactivate() {}
マウスカーソルをホバーさせた際の振る舞いを拡張するにあたっては、vscode.languages.registerHoverProvider
を利用します。provideHover
メソッドをオーバーライドして、オリジナルの実装をする形で目的の機能が実装できます。
/**
* Unicode文字のホバープレビューを提供するプロバイダークラス
*/
class UnicodeHoverProvider implements vscode.HoverProvider {
/**
* ホバー情報を提供します
* @param document - テキストドキュメント
* @param position - カーソル位置
* @param token - キャンセルトークン
* @returns ホバー情報
*/
provideHover(
document: vscode.TextDocument,
position: vscode.Position,
_: vscode.CancellationToken,
): vscode.ProviderResult<vscode.Hover> {
// 略
}
ホバー時に表示される領域の内容は基本Markdownで記載します。使用可能なタグが限定されますがHTMLも利用できますので、Markdownの表現能力に不足がある場合はHTMLの使用も選択肢になります。
// カスタムフォントスタイリングでHTMLを作成
const html = `<div style="font-family: '${fontFamily}', sans-serif; font-size: ${fontSize}px; text-align: center; padding: 10px;">
<div style="font-size: ${fontSize * 2}px; margin-bottom: 10px;">${character}</div>
<div style="font-size: 12px; color: #888;">
<strong>Unicode:</strong> U+${unicodeValue.toString(16).toUpperCase().padStart(4, "0")}<br />
<strong>Decimal:</strong> ${unicodeValue}<br />
<strong>Character:</strong> ${charName}<br />
<strong>See:</strong><a href="https://symbl.cc/en/${unicodeValue.toString(16).toUpperCase().padStart(4, "0")}/" target="_blank"> symbl.cc</a>
</div>
</div>`
const markdown = new vscode.MarkdownString(html)
markdown.supportHtml = true
markdown.isTrusted = true
全体は以下のコードをご覧ください。
- src/extension.ts: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/src/extension.ts
2.4 デバッグとテスト
F5
キーを押すだけで、現在開発中の拡張機能が読み込まれた新しいVSCodeウィンドウが立ち上がり、すぐにデバッグを開始できます。デバッグではデバッガーがアタッチされたVSCodeを立ち上げます。tscでトランスパイルされた結果出力されたJSファイルを使ってプラグインがインストールされた状態を仮想的に実現し、その中で動作確認を行う形です。
また、VSCodeのデバッグの構成ではデバッグする前にプレタスクを定義することができます
。tscを実行した上でデバッガーを立ち上げたい場合にボタン一つでその2つを同時に実行させることができて便利です。
自分は今回のタイミングで解決できなかったのですが、pnpmをmiseなどのランタイムマネージャーを使ってゴニョゴニョしていると、プレタスクが発行するbashコマンドの実行時にpnpmを解決できなくって失敗してしまうことがありました。.bashrc
を整備する、zshを使うようにするなど何か解決方法はあると思うのです、本筋ではないので諦めました。ご参考まで。
-
.vscode/launch.json: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/.vscode/launch.json
{ // IntelliSense を使用して利用可能な属性を学べます。 // 既存の属性の説明をホバーして表示します。 // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "args": ["--extensionDevelopmentPath=${workspaceFolder}"], "name": "拡張機能の起動", // miseでlocalにpnpmを入れているとVSCodeがpnpmを見つけられないので一旦コメントアウト // "preLaunchTask": { // "type": "npm", // "script": "vscode:prepublish" // }, "outFiles": ["${workspaceFolder}/out/**/*.js"], "request": "launch", "type": "extensionHost" } ] }
2.5 試験利用
マーケットプレイスに公開する前に実際に配布物の状態(VSIXファイル)で自環境に導入して試験利用したいと思いました。その場合は、コマンドでVSIXファイルを作成し、VSCodeのExtensionsからVSIXでインストールすることで行うことができます。
npx vsce package
3. LLM(Claude Code)との協調開発で爆速プロトタイピング
2.1に記載のプロンプトを用いることで、一般に提供される雛形よりも先に進んだところから作業をスタートさせることができました。爆速と言っても過言ではないでしょう。
多くの場合、右も左もわからない状態だとZennやQiitaの記事などからソースをコピペしつつ公式ドキュメントを横に置いて確認しながら作業することになるでしょう。そのコピペの部分をLLMにやってもらうことができるのはかなり体験が良かったです。新しい技術スタックに飛び込む際の学習コストを劇的に下げてくれました。
4. 品質を高めるための手直しと技術的工夫
プロトタイプとしては優秀ではありますが、一方で、プロダクションコードとして見てみると物足りない点が多々ありました。
4.1 そもそも実装が間違っている
主要な間違いは以下でした。特に3つ目の解消は時間がかかりました。Stack Overflowくんのお陰で解消しました。2つ目とかはヒアドキュメントを使うときのあるあるミスですね。
- テンプレートリテラルを使うよう実装しているが、波括弧で変数名が囲われていない
- ヒアドキュメントで書く際、先頭行を空行にして改行しているためVSCodeで表示するとrawなテキストで表示されてします
- 最新の仕様では
supportHtml
だけではなくisTrusted
もtrue
にしなければならないようだができていない
- src/extension.ts: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/src/extension.ts
4.2 エディタなど各種設定と静的解析
4.2.1 エディタ
先のデバッグと実行の設定と同様に開発環境を整備する観点では特に何もアシストはなかったので、自分のこれまでの経験をもとに拡張の選定及び設定を行いました。
- .vscode/settings.json: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/.vscode/settings.json
- .vscode/extensions.json: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/.vscode/extensions.json
4.2.2 ランタイム、TypeScript、静的解析
その他にもランタイムマネージャーを利用するようにしたり、TSの設定をしたり、今回採用したリンター兼フォーマッターであるbiome(かつてromaって名前でしたね)の設定なども自分でおこなっています。
-
ランタイム
- mise.toml: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/mise.toml
- .npmrc: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/.npmrc
- package.json: https://github.com/trustart-co-jp/unicode-hover-preview/blob/ddac78c9f065a9faf072ac734a94ef81fa538548/package.json#L6-L11
- TypeScript
- 静的解析
ちなみに、拡張機能のBiomeでフォーマットした時と、CLIでフォーマットした際に結果が異なるということがありました。VSCodeのデフォルトフォーマッターの設定ではJSONファイルなど一部の拡張子のファイルで設定が効かないという仕様を知らなかったことが原因でした。拡張子ごとの個別設定で明示的にbiomeを使うよう指定することで解消しました。
4.3 CI/CDの構築(GitHub Actions)
継続的インテグレーション・継続的デリバリー(CI/CD)は、開発プロセスの自動化と品質保証に不可欠です。GitHub Actionsを用いて、以下のワークフローを構築しました。
- .github/workflows/ci.yaml: https://github.com/trustart-co-jp/unicode-hover-preview/blob/main/.github/workflows/ci.yaml
これらの自動化により、手動での作業ミスを減らし、開発に集中できる環境を整えることができました。
5. まとめ
VSCode拡張機能は、JavaScript/TypeScriptの知識があれば比較的容易に開発できます。
ただ、プラグインを使う情報は多いですが開発する情報は多くはなく、目的の情報に辿り着くのはそれ以外の開発と比べて大変かなと思いました。
また、LLMの使い所としてプロトタイプといった叩き台作成に使うのは想像以上に便利です。Claude Code自体が飛び抜けて優秀っていうのもありますが。また、プロトタイプなど0から1を作る段階では外的要因を気にする必要がない(例:並行作業や既存資源との平仄を取るなど)ので、LLMに思う存分動いてもらいやすいんじゃなかろうかと思いました。
最後に
TRUSTART株式会社は、一緒に働くメンバーを募集しています!インターンメンバーも大募集中です!
興味をお持ちいただけた方は、ぜひ弊社の採用ページをご確認ください。

「人とデータで全てを可能にする」 不動産領域に関連する、あらゆるビジネスのDX化にチャレンジし、「テクノロジー×人」の力で社会課題を解決するTRUSTART株式会社のテックブログです! 採用情報はこちら:trustart.co.jp/recruit/
Discussion