5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KDDI Engineer & DesignerAdvent Calendar 2023

Day 17

「”例の神”を降臨させるVSCode拡張機能」をChatGPTに訊きながら作ってみた

Last updated at Posted at 2023-12-16

変なものを作ってお茶を濁す!

こんにちは!もういつの間にかクリスマスです!🎄
令和は本当に時空の流れが速くてヤバすぎますね~

今回は…ChatGPTに訊きながらVSCode拡張機能の開発についてトライしてみました!😊
今年に入って仕事でもプライベートでも大活躍したChatGPTですが、本当にちょっとしたものを作るのには全く苦労しなくなった印象です!
今回もネタ作りのために活躍していただくぞ…😋

この記事は以下のいずれかに当てはまる人向けです

  • ChatGPTを使って急いで何かつくらないといけないが、コツがわからない
  • 気まぐれでVSCode拡張機能を作ろうと思っている
  • とにかく暇

つくったもの

これです!

11.JPG

こちらからインストールできます!

つかいかた

  1. Ctrl+Shift+pでコマンドパレットを出して、Uimama Windowを開く
  2. Shift+zキーで「ずきゅーん」→後ろ向きダンス
  3. Shift+bキーで「ばきゅーん」→前向きダンス

<※注意>所属する団体とは一切関係がないし個人的に学習目的で勝手に作ったものです!🙄

下準備

まずは本家様から素材を拝借します!

か、かわいい~~!!!

AviUtlで適当にクロマキー合成などしつつ、動画フレームをいくつか抜き取っていきます…!
一応AviUtlの導入・使い方の解説ページも載せときますね。

エンジニアでもこの時代に軽い動画編集ぐらいできておいて損はないはず?🙄
(こんな優秀なソフトウェアが太古の時代から存在してしかも無料というのがすごい)

mp4で書き出したら、Adobe ExpressでGIF画像に変換します!

image.png

はいできました!作ったのが、

  • uimama1.gif(正面向きでぴょんぴょん踊る)
  • uimama2.gif(ずきゅーん!をしたあと後ろを向くモーション)
  • uimama3.gif(ばきゅーん!をしたあと前を向くモーション)
  • uimama4.gif(後ろ向きでぴょんぴょん踊る)

の4つのGIF素材です。
(もっと作りたいけど…大変なのでやめておきます。)

ミリ知らでVSCode拡張機能を作る(勉強編)

どうやって始めればいい…?

もう既にやり切った感すら出ていますが、まずはVSCode拡張機能の作り始め方を調べましょう…

nodejsをいれて、yoというのを入れると新規プロジェクトが作れるらしいぞ…😊
え、ChatGPTはどうしたって? 人間RAGです。お前が人工知能になるんだよ!

人間が使いやすい対話シェルに従って進めます…

2.JPG

無事プロジェクトが作成され、開きました。嬉しい。
3.JPG

GIF画像表示の例ないかな…?

そもそもVSCode内に画像を表示するってできるんでしょうか?🙄

あ、簡単な画像を動かしているVSCode拡張機能がありました…!
記事も初学者向けで大変助かります!

githubを公開されているので、参考にしていきます。
コードの中身は…WEBVIEWを出してますね!なるほどなるほど。

キーボード入力に反応させるには?

キーボード入力に反応して画像が変わる仕組みを実装したいんですが、どうすればいいんでしょうか…?🙄

package.jsonファイルを変更して、"keybindings"を追加すればよいみたいですね。ふむふむ…
それにしても…お手製のドキュメントはポイントがまとまっていて読みやすいですね!
人間最高!
🤗

ミリ知らでVSCode拡張機能を作る(制作編)

ここまでの人間RAGの成果として、以下のことが分かりました。

  • VSCode拡張機能の本体はextension.tsで、他はあんまり触らなくてよさそう。
  • GIF画像表示の実装で簡単なものに、ウィンドウ内にWEBVIEWを出す方法がある。
  • キーバインディングの実装は、package.jsonに書く必要がありそう。

満を持してChatGPT3.5に投げていきます。😎<やれ
今回はpackage.json側のキーバインディングの部分だけ作っておいて、extension.tsの実装を少しずつやってもらうことにしました。

人間.txt
以下は、VSCodeのウィンドウで、特定のGIF画像を表示させる拡張機能のスクリプトです。
以下はextension.tsです。
~~
import * as vscode from 'vscode';

...省略(JustDoItExtensionのコードを参考に何となく作ったもの)...

以下は、キーバインディングを実装したpackage.jsonです。
~~
"commands": [
...省略...
		"keybindings":[
            {
                "command":"extension.Zukyun",
                "key": "shift+z"
            },
			{
                "command":"extension.Bakyun",
                "key": "shift+b"
            }
        ]       
...省略...

ここで、extension.ts内にextension.Zukyunを実装し、特定のGIF画像をuimama1.gifから、
uimama2.gifに切り替える処理を実装したいと思います。
extension.tsのコードの機能追加、修正をお願いします。

次のように、extension.Zukyunを追加して、FUNCTION_NAMEを利用してGIF画像を切り替えるようにできます。

extension.ts

    (中略)

まず、「ずきゅーん」の切り替えだけやってもらう関数を作ってもらいました。
あんまり一遍に色々実装させすぎないのがコツだと思ってます。

人間.txt
動作しました。
さらに、extension.ts内にextension.Bakyunを実装し、
特定のGIF画像をuimama3.gifに切り替える処理を実装したいと思います。
extension.tsのコードの機能追加、修正をお願いします。

もちろんです。以下は、extension.Bakyunを実装して、新しいGIF (uimama3.gif) を切り替える処理を追加したものです。

extension.ts

    (中略)

「ばきゅーん」も実装してもらいました。
このままだと、ずきゅーんもばきゅーんも一生繰り返してしまうので、ずきゅーんの後は後ろ向きで踊るGIFに、ばきゅーんの後は前向きで踊るGIFに変更してもらいます。(恐らくsetTimeout的な実装をしてもらえるだろうという予想をしながら訊きます。)

人間.txt
動作しました。
さらに、extension.Zukyunを以下の仕様に変更したいと思います。
・特定のGIF画像をuimama2.gifに変更したあと、1500ms後にuimama4.gifに切り替える
extension.Bakyunも、以下の仕様に変更したいと思います。
・特定のGIF画像をuimama3.gifに変更したあと、1300ms後にuimama1.gifに切り替える

理解しました。以下はそれぞれのコマンドが変更された仕様に合わせた extension.Zukyun および extension.Bakyun の変更点です。

extension.ts
    (中略)

これで、extension.Zukyunを実行すると、uimama2.gifに切り替わり、1500ms後にuimama4.gifに変更されます。同様に、extension.Bakyunを実行すると、uimama3.gifに切り替わり、1300ms後にuimama1.gifに変更されます。

できたようなので、出てきたものをextension.tsにコピペし、デバッグを実行します。
はい、動きました…(汗
そもそもこんなしょうもないやり取りで動くものが出てくるのがすげぇんですけど…😂

できあがったコードはこちらです。(若干いじってますが、ほぼそのままです)

extension.ts
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as path from 'path';

export function activate(context: vscode.ExtensionContext) {

    let currentGif = 1; // Track the current GIF being displayed

    context.subscriptions.push(
        vscode.commands.registerCommand('extension.uimama', () => {
            const panel = vscode.window.createWebviewPanel(
                'uimama',
                'Ui-Mama',
                vscode.ViewColumn.One, {}
            );

            const onDiskGifPath1 = vscode.Uri.file(path.join(context.extensionPath, 'media/', 'uimama1.gif'));
            const onDiskGifPath2 = vscode.Uri.file(path.join(context.extensionPath, 'media/', 'uimama2.gif'));
            const onDiskGifPath3 = vscode.Uri.file(path.join(context.extensionPath, 'media/', 'uimama3.gif'));
            const onDiskGifPath4 = vscode.Uri.file(path.join(context.extensionPath, 'media/', 'uimama4.gif'));

            const gifsrc1 = onDiskGifPath1.with({ scheme: 'vscode-resource' });
            const gifsrc2 = onDiskGifPath2.with({ scheme: 'vscode-resource' });
            const gifsrc3 = onDiskGifPath3.with({ scheme: 'vscode-resource' });
            const gifsrc4 = onDiskGifPath4.with({ scheme: 'vscode-resource' });

            panel.webview.html = getWebviewContent(gifsrc1);

            vscode.window.showInformationMessage('Ui-Mama (9 years old)');

            // Bind Zukyun to switch between GIFs with a delay
            vscode.commands.registerCommand('extension.Zukyun', () => {
                panel.webview.html = getWebviewContent(gifsrc2);
                currentGif = 2; // Set the current GIF to 2

                // Switch to uimama4.gif after a delay of 1500ms
                setTimeout(() => {
                    panel.webview.html = getWebviewContent(gifsrc4);
                    currentGif = 4; // Set the current GIF to 4
                }, 1500);
            });

            // Bind Bakyun to switch to the third GIF with a delay
            vscode.commands.registerCommand('extension.Bakyun', () => {
                panel.webview.html = getWebviewContent(gifsrc3);
                currentGif = 3; // Set the current GIF to 3

                // Switch to uimama1.gif after a delay of 1300ms
                setTimeout(() => {
                    panel.webview.html = getWebviewContent(gifsrc1);
                    currentGif = 1; // Set the current GIF to 1
                }, 1300);
            });
        })
    );
}

function getWebviewContent(gifsrc: vscode.Uri) {
    return `<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource:; style-src vscode-resource:;">
        <title>Ui-Mama</title>
    </head>
    <body>
        <h1>Ui-Mama (9 years old)</h1>
        <div class="container">
            <img src="${gifsrc}" class="gif-image" id="gif1">
        </div>
    </body>
    </html>`;
}

// This method is called when your extension is deactivated
export function deactivate() {}

内容は…getWebviewContentというWEBVIEW出す関数を作って、キーで呼ばれた時は別のGIFを引数で渡してWEBVIEWを新しく作る、という感じみたいですね。特にさしたる問題もなさそうです。
(細かいこと言うと、ウィンドウを閉じたり複数開いたりするとバグがでるみたい)

一応、package.jsonも参考に貼っておきます。keybindingsを追加したのと、enginesのバージョンが高いとGIFを読み込まなかったので、ちょっとだけ低めのバージョン(1.51.0)を指定しています。

package.json
{
  "name": "uimama-window",
  "displayName": "uimama-window",
  "description": "Enjoy dancing Uimama !",
  "version": "0.0.1",
  "publisher": "nazunaNyanmage",
  "engines": {
    "vscode": "^1.51.0"
  },
  "categories": [
    "Other"
  ],
  "activationEvents": [],
  "main": "./out/extension.js",
	"contributes": {
		"commands": [
			{
				"command": "extension.uimama",
				"title": "Ui-mama Window"
			}
		],
		"keybindings":[
            {
                "command":"extension.Zukyun",
                "key": "shift+z"
            },
			{
                "command":"extension.Bakyun",
                "key": "shift+b"
            }
        ]       
	},
  "scripts": {
    "vscode:prepublish": "npm run compile",
    "compile": "tsc -p ./",
    "watch": "tsc -watch -p ./",
    "pretest": "npm run compile && npm run lint",
    "lint": "eslint src --ext ts",
    "test": "node ./out/test/runTest.js"
  },
  "devDependencies": {
    "@types/vscode": "^1.51.0",
    "@types/mocha": "^10.0.3",
    "@types/node": "18.x",
    "@typescript-eslint/eslint-plugin": "^6.9.0",
    "@typescript-eslint/parser": "^6.9.0",
    "eslint": "^8.52.0",
    "glob": "^10.3.10",
    "mocha": "^10.2.0",
    "typescript": "^5.2.2",
    "@vscode/test-electron": "^2.3.6"
  }
}

ミリ知らでVSCode拡張機能を作る(公開編)

最後に、こちらの記事を参考に、この拡張機能を公開して一通りとなります!

まずはマーケットプレイスに投稿する権限を持ったトークンを作ります。

5.JPG

Createを押すと、ランダム文字列なトークンが表示されるのでメモします。

続けてプロジェクトフォルダからpublisherを登録…
と思ったんですがcreate-publisherコマンドは今は使えないそうです。

6.JPG

言われたページに行き、publisher登録をします。https://aka.ms/vscode-create-publisher

7.JPG

で、プロジェクトフォルダに戻り、publishコマンドを打ちます。
さっきもらったトークンを記入して…

10.JPG

無事Publishされました!(package.json内にリポジトリの登録がなくてWARNINGが出てましたので、あとで記載しました。拡張機能のインストールページにリンクがでるみたいですね。)
2,3分経つと、公開されましたよ~というメールが飛んできますので、そこからダウンロードページを確認できます。スマホアプリに比べたら随分簡単ですね!
早速ダウンロードしてみて、動くことを確認できたら終了です。お疲れさまでした🤗

おわりに

最近は皆もChatGPTに慣れてきていて、AI便利だけどコツがいるぞ、という意見が目立つようになってきました🤔

今回の記事も、一見超スムーズに開発をしたかのような書き方をしていますが実はそんなことはなくて、例えば、「キー入力に反応させる」実装をpackage.jsonではなくWEBVIEWの中に実装しようとして動かない…というようなハマり方などしてました🙄(VSCode内のWEBVIEWがSandBoxになっていて、一般的なjavascriptを動作させるのが難しいみたいです。)

なので一旦真面目にググってから再トライ、という流れをとっています。急がば回れ。 ぼんやりでも解答が想像できる状態で依頼を投げるのがよさそう…という感じですね!

しかし本当に…とんでもない時代になりました!
興味や動機さえあればもう結構なことができそうな感じです!
(これが子供の頃から当たり前に使える世代はいったいどうなるんでしょうか…?!)
それではどうぞ充実したChatGPTライフをお過ごしください…😋

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?