6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Chrome拡張機能を作るのを手助けするvscodeの拡張機能を作ったよ。

Last updated at Posted at 2022-11-14

はじめに

VscodeでChrome拡張機能作るのって意外と大変ですよね。Chrome拡張機能のAPIに対応した補完機能を持つVscodeの拡張機能が多分無いからです。ということで、試しに作ってみたというのがこの記事の内容です。

制作物

とりあえず、ChromeRuntime、ChromeTabs、ChromeContextMenu、ChromeStorageなどは対応しています。結局、自分が主に使うものしかまだ対応できていませんが、今後は増やしていく予定です。

技術面

使用した技術

  • JavaScript
  • nodejs

作り方

下記のサイトを参考にして作りました。

主に書いたコードはたったこれだけ。とりあえず、補完機能を作る拡張機能で使いたいときは、CompletionItemとやらを使います。詳しくは割愛します。

extension.js
const vscode = require('vscode');

function makeSnippets(apiName, keywords, items) {
    for (let key in keywords) {
        const template = {
            label: key,
            insertText: new vscode.SnippetString(keywords[key]),
            kind: null,
            documentation: null,
            detail: ''
        }
        let urlWord = key.replace("chrome", "").replace(apiName, "");
        urlWord = urlWord[0].toLowerCase() + urlWord.replace(urlWord[0], "");
        if (key.indexOf("On") == -1) {
            template.kind = vscode.CompletionItemKind.Method;
            template.documentation = new vscode.MarkdownString("https://developer.chrome.com/docs/extensions/reference/" + apiName.toLowerCase() + "/#method-" + urlWord);
            items.push(template);
        } else {
            template.kind = vscode.CompletionItemKind.Event;
            template.documentation = new vscode.MarkdownString("https://developer.chrome.com/docs/extensions/reference/" + apiName.toLowerCase() + "/#event-" + urlWord);
            items.push(template);
        }
    }
}

class JsCompletionItemProvider {
    constructor() {
        this.completionItems = [];
        const keywordsOfRuntime = { chromeRuntimeOnUpdateAvailable: "chrome.runtime.onUpdateAvailable.addListener(function (${1:details}) {\n\t$2\n});", chromeRuntimeOnSuspendCanceled: "chrome.runtime.onSuspendCanceled.addListener(function () {\n\t$1\n});", chromeRuntimeOnSuspend: "chrome.runtime.onSuspend.addListener(function () {\n\t$1\n});", chromeRuntimeOnStartup: "chrome.runtime.onStartup.addListener(function () {\n\t$1\n});", chromeRuntimeOnRestartRequired: "chrome.runtime.onRestartRequired.addListener(function (${1:reason}) {\n\t$2\n})", chromeRuntimeOnMessageExternal: "chrome.runtime.onMessageExternal.addListener(function (${1:message}, ${2:sender}, ${3:sendResponse}) {\n\t$4\n})", chromeRuntimeOnInstalled: "chrome.runtime.onInstalled.addListener(function (${1:details}) {\n\t$2\n});", chromeRuntimeOnConnectNative: "chrome.runtime.onConnectNative.addListener(function (${1:port}) {\n\t$2\n});", chromeRuntimeOnConnectExternal: "chrome.runtime.onConnectExternal.addListener(function (${1:port}) {\n\t$2\n});", chromeRuntimeOnConnect: "chrome.runtime.onConnect.addListener(function (${1:port}) {\n\t$2\n})", chromeRuntimeSetUninstallURL: "chrome.runtime.setUninstallURL(${1:url}, function () {});", chromeRuntimeNativeMessage: "chrome.runtime.sendNativeMessage(${1:application}, ${2:message}, function (${3:response}) {\n\t$4\n):", chromeRuntimeRestartAfterDelay: "chrome.runtime.restartAfterDelay(${1:seconds}, function () {});", chromeRuntimeRestart: "chrome.runtime.restart();", chromeRuntimeReload: "chrome.runtime.reload()", chromeRuntimeOpenOptionsPage: "chrome.runtime.openOptionsPage(function (${1:lastError}) {\n\t$2\n});", chromeRuntimeGetPlatformInfo: "chrome.runtime.getPlatformInfo(function (${1:platformInfo}) {\n\t$2\n})", chromeRuntimeGetPackageDirectoryEntry: "chrome.runtime.getPackageDirectoryEntry(function (${1:directoryEntry}) {\n\t$2\n})", chromeRuntimeGetManifest: "chrome.runtime.getManifest();", chromeRuntimeGetBackgroundPage: "chrome.runtime.getBackgroundPage(function (${1: window}) {\n\t$2\n});", chromeRuntimeConnectNative: "chrome.runtime.connectNative(${1:application});", chromeRuntimeConnect: "chrome.runtime.connect(${1:extensionId}, ${2:connectInfo});", chromeRuntimeSendMessage: "chrome.runtime.sendMessage({$1}, function (${2:response}) {\n\t$3\n});", chromeRuntimeOnMessage: "chrome.runtime.onMessage.addListener(function(${1:request}, ${2:sender}, ${3:sendResponse}) {\n\t$4\n});", chromeRuntimeGetURL: "chrome.runtime.getURL($1);" };
        const keywordsOfTabs = { chromeTabsOnZoomChange: "chrome.tabs.onZoomChange.addListener(function (${1:zoomChangeInfo}) {\n\t$2\n});", chromeTabsOnUpdated: "chrome.tabs.onUpdated.addListener(function (${1:tabId}, ${2:changeInfo}, ${3:tab}) {\n\t$4\n});", chromeTabsOnReplaced: "chrome.tabs.onReplaced.addListener(function (${1:addedTabId}, ${2:removedTabId}) {\n\t$3\n});", chromeTabsOnRemoved: "chrome.tabs.onRemoved.addListener(function (${1:tabId}, ${2:removeInfo}) {\n\t$3\n});", chromeTabsOnMoved: "chrome.tabs.onMoved.addListener(function (${1:tabId}, ${2:moveInfo}) {\n\t$3\n});", chromeTabsOnHighlighted: "chrome.tabs.onHighlighted.addListener(function (${1:highlightInfo}) {\n\t$2\n});", chromeTabsOnDetached: "chrome.tabs.onDetached.addListener(function (${1:tabId}, ${2: detachInfo}) {\n\t$3\n});", chromeTabsOnCreated: "chrome.tabs.onCreated.addListener(function (${1:tab}) {\n\t$2\n});", chromeTabsOnAttched: "chrome.tabs.onAttached.addListener(function (${1:tabId}, \${2:attachInfo}) {\n\t$3\n});", chromeTabsOnActivated: "chrome.tabs.onActivated.addListener(function (${1:activeInfo}) {\n\t$2\n});", chromeTabsUpdate: "chrome.tabs.update(${1:tabId}, ${2:updateProperties}, function (${3:tab}) {\n\t$4\n});", chromeTabsUngroup: "chrome.tabs.ungroup(${1:tabIds}, function () {});", chromeTabsSetZoomSettings: "chrome.tabs.setZoomSettings(${1:tabId}, ${2:zoomSettings}, function () {});", chromeTabsSetZoom: "chrome.tabs.setZoom(${1:tabId}, ${2:zoomFactor}, function () {});", chromeTabsRemove: "chrome.tabs.remove(${1:tabIds}, function () {});", chromeTabsReload: "chrome.tabs.reload(${1:tabId}, ${2:reloadProperties}, function () {});", chromeTabsMove: "chrome.tabs.move(${1:tabIds}, ${2:moveProperties}, function (${3:tabs}) {\n\t$4\n});", chromeTabsHighlight: "chrome.tabs.highlight(${1:highlightInfo}, function (${2:window}) {\n\t$3\n});", chromeTabsGroup: "chrome.tabs.group(${1:options}, function (${2:groupId}) {\n\t$3\n});", chromeTabsGoForward: "chrome.tabs.goForward(${1:tabId}, function () {});", chromeTabsGoBack: "chrome.tabs.goBack(${1:tabId}, function () {});", chromeTabsGetZoomSettings: "chrome.tabs.getZoomSettings(${1:tabId}, function (${2:zoomSettings}) {\n\t$3\n});", chromeTabsGetZoom: "chrome.tabs.getZoom(${1:tabId}, function (${2:zoomFactor}) {\n\t$3\n});", chromeTabsGetCurrent: "chrome.tabs.getCurrent(function (${1:tab}) {\n\t$2\n});", chromeTabsGet: "chrome.tabs.get(${1:tabId}, function (${2:tab}) {\n\t$3\n});", chromeTabsDuplicate: "chrome.tabs.duplicate(${1:tabId}, function (${2:tab}) {\n\t$3\n});", chromeTabsDiscard: "chrome.tabs.discard(${1:tabId}, function (${2:tab}) {\n\t$3\n});", chromeTabsDetectLanguages: "chrome.tabs.detectLanguage(${1:tabId}, function (${2:languages}) {\n\t$3\n});", chromeTabsCreate: "chrome.tabs.create(${1:createProperties}, function (${2:tab}) {\n\t$3\n});", chromeTabsConnect: "chrome.tabs.connect(${1:tabId}, ${2:connectInfo});", chromeTabsCaptureVisibleTab: "chrome.tabs.captureVisibleTab(${1:windowId}, ${2:options}, function (${3:dataUrl}) {\n\t$4\n});", chromeTabsQuery: "chrome.tabs.query(${1:queryInfo}, function (${2:tabs}) {\n\t$3\n});", chromeTabsOnUpdated: "chrome.tabs.onUpdated.addListener(function (${1:tabId}, ${2:changeInfo}, ${3:tab}) {\n\t$4\n});", chromeTabsSendMessage: "chrome.tabs.sendMessage(${1:tabId}, ${2:message}, ${3:options}, function(${4:response}) {\n\t$5\n});" };
        const keywordsOfStorage = { chromeStorageLocalSet: "chrome.storage.local.set({$1}, function () {});", chromeStorageSyncSet: "chromestorage.sync.set({$1}, function () {});", chromeStorageLocalGet: "chrome.storage.local.get([$1], function (${2:result}) {\n\t$3\n});", chromeStorageSyncGet: "chrome.storage.sync.get([$1], function (${2:result}) {\n\t$3\n});" };
        const keywordsOfBrowserAction = { chromeBrowserActionOnClicked: "chrome.browserAction.onClicked.addListener(function () {\n\t$2\n});" }
        const keywordsOfContextMenus = { chromeContextMenusOnClicked: "chrome.contextMenus.onClicked.addListener(function (${1:info}, ${2:tab}) {\n\t$3\n});", chromeContextMenusUpdate: "chrome.contextMenus.update(${1:id}, ${2:updateProperties}, function () {});", chromeContextMenusRemoveAll: "chrome.contextMenus.removeAll(function () {});", chromeContextMenusRemove: "chrome.contextMenus.remove(${1:menuItemId}, function () {});", chromeContextMenusCreate: "chrome.contextMenus.create(${1:createProperties}, function () {});",  };
        makeSnippets("Runtime", keywordsOfRuntime, this.completionItems);
        makeSnippets("Tabs", keywordsOfTabs, this.completionItems);
        makeSnippets("Storage", keywordsOfStorage, this.completionItems);
        makeSnippets("BrowserAction", keywordsOfBrowserAction, this.completionItems);
        makeSnippets("ContextMenus", keywordsOfContextMenus, this.completionItems);
        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: 'javascript' },
            new JsCompletionItemProvider(),
            '.'
        )
    );
}

function deactivate() { }

module.exports = {
    activate,
    deactivate
}


詰まったところ

  • 最初のHello Worldとコマンドで表示させるのがなぜかできなかった
    これは、このVscodeの拡張機能の問題ではなく、「試しに拡張機能を作ってみよう! 」みたいな初めて拡張機能を作るときに行う作業がうまくいかなかったという問題です。
    Vscodeの拡張機能を作るときにコマンドで最初に拡張機能のひな型を作成するのですが、作成してできた新しいフォルダー内でデバッグしなきゃいけないのに、一つ上のディレクトリでデバッグしてしまったのです。
    こういう初歩的なミスはなくしたいところです。

  • SnippetStringを知らなかった
    vscodeの拡張機能ではSnippetStringが使えますが、new vscode.SnippetString(入力したいテキスト)としないと適用されません。自動で適用すると思っていました。

  • 拡張機能のアイコンをどこで設定するのかわからなかった
    意外と調べても出てこなくて、数時間格闘しておりました。めちゃめちゃ単純でした。

{
    "name": "chromextnsionsnippets",
    "displayName": "ChromeExtensionSnippets",
    "description": "support and help making chrome extension",
    "version": "0.0.1",
    "icon": "icon.png", /*これを追加すればOK!*/
    /*以下省略*/
}

使い方

もちろんインストールするのみ。
ちなみに使用してみると↓みたいな感じになります。
usage.gif

とりあえず使いたいAPIがあるなら、そのAPIのドットを消したコードを打ち込めばOK。
例えば、chrome.tabs.queryが使いたかったら、chrometabsqueryと入力するだけ。

おわりに

はじめにも書いたように徐々に対応するAPIは増やしていきます。次に追加しようと思うのはactionなのですが、このAPIを先に追加してほしいという要望がもしあれば、コメントよろしくお願いします<(_ _)>

ここまで読んでくださりありがとうございます。私自身、初めてのQiitaの記事の投稿で非常に緊張しており、文章もつたないですが、温かい目で見守ってください。

6
6
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
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?