vscode插件開發(fā)實(shí)踐

初次嘗試了一下vscode插件開發(fā),寫了一個(gè)前端工具箱作為日常方便使用,記錄一下

image

在vscode插件里面搜索:前端工具箱定拟∮谖ⅲ或打開該網(wǎng)址安裝即可 https://marketplace.visualstudio.com/items?itemName=poetries.fe-tools

搭建開發(fā)環(huán)境

我們先準(zhǔn)備開發(fā)環(huán)境。我使用的操作系統(tǒng):mac青自,首先確保安裝了VS Code株依、Node.js 和 Git:

code -v
node -v
npm -v
git --version

https://code.visualstudio.com/api/get-started/your-first-extension

npm install -g yo generator-code

使用yo code指令初始化VSCode插件項(xiàng)目,然后需要回答一些簡單的配置問題

yo code

# What type of extension do you want to create? 
# 創(chuàng)建那一種類型的擴(kuò)展延窜?

# What's the name of your extension?
# 擴(kuò)展的名稱恋腕?

# What's the identifier of your extension?
# 擴(kuò)展的標(biāo)示?

# What's the description of your extension?
# 擴(kuò)展的描述

# Initialize a git repository? 
# 初始化git倉庫

# Which package manager to use? 
# 使用那一種包管理器

運(yùn)行插件

使用 VS Code 打開項(xiàng)目逆瑞,在編輯器內(nèi)部荠藤,按F5,編譯并打開一個(gè)“擴(kuò)展開發(fā)宿主機(jī)”窗口運(yùn)行調(diào)試插件获高。為了敘述方便哈肖,把新打開的窗口稱為運(yùn)行窗口,舊窗口稱為編輯窗口念秧。在新窗口的命令面板(Ctrl+Shift+P) 運(yùn)行 Hello World 命令淤井。看到右下角的 Hello World 通知信息了嗎摊趾?恭喜你已經(jīng)運(yùn)行了一個(gè)自己編寫的插件币狠!

image

調(diào)試插件

使用 VS Code 調(diào)試擴(kuò)展插件很容易。這里演示一下如何設(shè)置斷點(diǎn)砾层。在編輯窗口打開 extension.js 文件漩绵, 點(diǎn)擊編輯器行號(hào)左側(cè)的邊欄設(shè)置斷點(diǎn)。在運(yùn)行窗口 命令面板輸入 Hello World 命令運(yùn)行插件肛炮,命中斷點(diǎn)止吐。

image

調(diào)試webview

按F5打開調(diào)試模式,在webview頁面铸董,按command+shift+p選擇open webview

image

image

項(xiàng)目解析

接下來我們來深入研究一下 helloworld 插件祟印。 helloworld 的功能很簡單,就是用戶可以在命令面板執(zhí)行 Hello World 命令粟害,輸出 Hello World 信息

從實(shí)現(xiàn)的角度來看蕴忆,helloworld 插件做了三件事:

  • 注冊激活事件 onCommand:extension.helloWorld:插件在extension.helloWorld 觸發(fā)時(shí)被激活。
  • 注冊貢獻(xiàn)點(diǎn) contributes.commands:extension.helloWorld:在命令面板中使能 hello world 命令悲幅,并將其綁定到 extension.helloworld套鹅。
  • 調(diào)用 VS Code API commands.registerCommand 給注冊的命令 extension.helloWorld綁定處理函數(shù)站蝠。

基本概念

簡單解釋一下上面提到的三個(gè)概念:激活事件、貢獻(xiàn)點(diǎn)和接口:

  • 激活事件卓鹿,譯自 Activation Events菱魔,在配置清單 package.json 中靜態(tài)聲明,其實(shí)就是 JSON 數(shù)組 activationEvents 的值吟孙。當(dāng)激活事件發(fā)生時(shí)澜倦,聲明的擴(kuò)展將被激活。以下是目前所有可用激活事件:onLanguage杰妓、onCommand藻治、onDebug:onDebugInitialConfigurationsonDebugResolve巷挥、workspaceContains桩卵、onFileSystemonView倍宾、onUri雏节、onWebviewPanel、*高职;
  • 貢獻(xiàn)點(diǎn)钩乍,譯自 Contribution Points,在配置清單 package.json 中靜態(tài)聲明初厚,貢獻(xiàn)點(diǎn)其實(shí)就是VS Code 的可以擴(kuò)展的功能點(diǎn)件蚕。以下是目前所有可用的貢獻(xiàn)點(diǎn):configurationconfigurationDefaults产禾、commandsmenus牵啦、keybindings亚情、languagesdebuggers哈雏、breakpoints楞件、grammarsthemes裳瘪、snippets土浸、jsonValidationviews彭羹、viewsContainers黄伊、problemMatchersproblemPatterns taskDefinitions派殷、colors还最、typescriptServerPlugins墓阀、resourceLabelFormatterscontributes.configur拓轻。
  • VS Code 接口:可以在擴(kuò)展代碼中調(diào)用的一組 JavaScript API斯撮。鏈接中列舉了所有可用的API,熟悉基本的扶叉,其他的用到的時(shí)候按需查找就行了勿锅。

一般來說,插件都會(huì)使用到這三個(gè)概念:激活事件枣氧、貢獻(xiàn)點(diǎn)VS Code API粱甫。接下來我們分析一下 HelloWorld 示例的源代碼,看看它是如何使用這些概念的作瞄。

JavaScript 插件目錄結(jié)構(gòu)

image

VS Code 插件的目錄結(jié)構(gòu)很簡單茶宵,根據(jù)命名大概就能知道作用。不同項(xiàng)目類型目錄結(jié)構(gòu)可能會(huì)有很大不同宗挥。對于 JavaScript 類型的項(xiàng)目來說乌庶,最重要的就是package.jsonextension.js

清單文件:package.json

每個(gè) VS Code 插件都必須有一個(gè)用來描述插件的清單文件 package.json契耿。VS Code 的清單文件是聲明式的 JSON 格式瞒大,用于聲明插件名(name)、插件展示名( displayName )搪桂、描述信息(description)透敌、版本( version )、引擎( engines )踢械、類別( categories )酗电、依賴( devDependencies )、腳本 (scripts)内列、貢獻(xiàn)點(diǎn)( contributes )撵术、入口文件( main )、激活事件( activationEvents )等话瞧。

下面是 helloworld 插件的清單文件內(nèi)容嫩与,為了便于理解,我加了一些注釋交排。

{
    "name": "helloworld", //插件名
    "displayName": "helloworld", // 插件市場顯示的插件名划滋,支持中文
    "description": "demo", // 插件市場顯示的描述信息
    "version": "0.0.1", // 版本號(hào)
    // 最低支持的 VS Code 版本
    "engines": {
        "vscode": "^1.38.0"
    },
    // 插件市場分類
    "categories": [
        "Other"
    ],
    // 激活事件
    "activationEvents": [
        "onCommand:extension.helloWorld"
    ],
    "main": "./extension.js", // 指定入口文件
    // 貢獻(xiàn)點(diǎn)
    "contributes": {
        "commands": [{
            "command": "extension.helloWorld",
            "title": "Hello World"
        }]
    },
    // 腳本
    "scripts": {
        "test": "node ./test/runTest.js"
    },
    // 依賴,包含版本信息
    "devDependencies": {
        "@types/glob": "^7.1.1",
        "@types/mocha": "^5.2.6",
        "@types/node": "^10.12.21",
        "@types/vscode": "^1.38.0",
        "eslint": "^5.13.0",
        "glob": "^7.1.4",
        "mocha": "^6.1.4",
        "typescript": "^3.3.1",
        "vscode-test": "^1.2.0"
    }
}

插件入口文件:extension.js

插件的入口文件是在清單文件中指定的埃篓,如果項(xiàng)目比較大处坪,源文件比較多,也可以統(tǒng)一放在 src 目錄里。

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
/* 導(dǎo)入 "vscode" 模塊稻薇,這個(gè)模塊包含 VS Code 的擴(kuò)展接口嫂冻。 */ 
const vscode = require('vscode');

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
// 第一次執(zhí)行命令時(shí),插件會(huì)被激活塞椎;插件被激活時(shí)桨仿,這個(gè)函數(shù)會(huì)被調(diào)用
// 必須在入口中實(shí)現(xiàn)這個(gè)函數(shù)。

/**
 * @param {vscode.ExtensionContext} context
 */
function activate(context) {

    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    /* 輸出診斷日志到控制臺(tái) */
    console.log('Congratulations, your extension "helloworld" is now active!');

    // The command has been defined in the package.json file
    // Now provide the implementation of the command with  registerCommand
    // The commandId parameter must match the command field in package.json
    /* 
     * 這個(gè)命令 extension.helloWorld 已經(jīng)在 package.json 文件中定義了案狠。
     * 現(xiàn)在我們使用 registerCommand 接口給這個(gè)命令綁定實(shí)現(xiàn)服傍。
     */
    let disposable = vscode.commands.registerCommand('extension.helloWorld', function () {
        // The code you place here will be executed every time your command is executed
        /* 每一次執(zhí)行命令,這兒的代碼都會(huì)執(zhí)行 */

        // Display a message box to the user
        vscode.window.showInformationMessage('Hello World!');
    });

    context.subscriptions.push(disposable);
}
exports.activate = activate;

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

module.exports = {
    activate,
    deactivate
}

一個(gè)插件必須在其主模塊實(shí)現(xiàn)并導(dǎo)出 activate()deactivate() 函數(shù)骂铁。

  • activate() 函數(shù) 初始化插件吹零。當(dāng)任何指定的激活事件發(fā)生時(shí),VS Code 會(huì)調(diào)用并且只調(diào)用它一次拉庵。
  • deactivate() 函數(shù) 清理插件灿椅。如果清理過程是異步的, deactivate() 函數(shù)必須返回一個(gè) Promise 對象钞支。如果清理運(yùn)行同步茫蛹,則 deactivate() 函數(shù)返回 undefined

Webview

插件可以分為多種烁挟,比如主題樣式類型的插件婴洼,圖標(biāo)插件,語言支持類型的插件撼嗓。Webview類型的插件只是Vscode插件的一個(gè)大類柬采。大致的實(shí)現(xiàn)大家可以參考文檔,文檔的示例使用的是html字符串且警,但這不適合復(fù)雜的Webview的開發(fā)粉捻。在GameNews這個(gè)插件中,模版部分振湾,我使用了vue以及pug杀迹。

// 靜態(tài)資源的目錄。絕對路徑押搪,并且使用了vscode-resource協(xié)議
// vscode-resource:/Users/Desktop/game-news/views
const webviewDir = path.join(context.extensionPath, 'views');

// 創(chuàng)建一個(gè)Webview的面板
const panel = vscode.window.createWebviewPanel(
    viewType,
    title,
    vscode.ViewColumn.One,
    {
        enableScripts: true, // 允許運(yùn)行js腳本,默認(rèn)是關(guān)閉的
        retainContextWhenHidden: true, // webview不可見時(shí)浅碾,腳本就會(huì)被掛起
        // 指定允許加載的本地資源的根目錄
        localResourceRoots: [vscode.Uri.file(webviewDir)]
    }
);

// 模版文件
const tpl = path.join(webviewDir, 'index.pug');

// 通過pug渲染模版文件大州,到webview上
panel.webview.html = pug.renderFile(tpl, options);

本地資源的使用

Webview中,我們會(huì)需要使用本地的css垂谢,js文件厦画。雖然可以使用行間js或者行間樣式,但是總歸不太好。使用本地文件根暑,就會(huì)涉及的靜態(tài)文件路徑的問題力试,在VScode中,我們需要使用絕對路徑排嫌。并且是vscode-resource協(xié)議的路徑畸裳。

const webviewDir = path.join(context.extensionPath, 'views');
// 靜態(tài)資源的絕對目錄
let URI = vscode.Uri.file(path.join(webviewDir, 'js', 'vue.js'))
// 使用vscode-resource協(xié)議頭
// 然后這個(gè)URL就可以使用在我們的webview的模版中了
URI = URI.with({ scheme: 'vscode-resource' });

Webview與插件通信

Webview相當(dāng)于一個(gè)網(wǎng)頁,而網(wǎng)頁是無法調(diào)用一些本地功能的淳地。但是插件本身是運(yùn)行在node環(huán)境的怖糊,而已我們可以通過插件實(shí)現(xiàn)一些在網(wǎng)頁中無法完成的功能。Webview如果通知插件呢颇象?這涉及到了Webview于網(wǎng)頁的通信機(jī)制伍伤。

下面是GameNews插件的例子,我通過事件將游戲新聞的url遣钳,發(fā)送給插件扰魂。插件會(huì)調(diào)用系統(tǒng)的命令,使用本地的瀏覽器打開url蕴茴。

// webview

// webview中劝评,一個(gè)內(nèi)置的全局api
const vscode = acquireVsCodeApi()

vscode.postMessage({
    command: 'preview',
    text: url
})
// 插件
panel.webview.onDidReceiveMessage(message => {
    switch (message.command) {
        case 'preview':
            // 打開瀏覽器
            open(message.text);
            return;
    }
}, undefined, context.subscriptions);

更多參考文檔:https://code.visualstudio.com/api/extension-guides/webview

打包、發(fā)布和升級(jí)

如何讓別人也能使用自己開發(fā)的插件呢荐开?這和移動(dòng)應(yīng)用開發(fā)一樣付翁,有兩種方式:

  • 把它發(fā)布到 VS Code 插件市場,這樣其他人就可以找到晃听、下載和使用你的插件百侧。
  • 或者,可以將插件打包為可安裝的vsix格式能扒,并與其他用戶共享佣渴。

vsce,簡寫自 Visual Studio Code Extensions初斑, 是用于打包辛润、發(fā)布和管理 VS Code 插件的命令行工具

先安裝 Node.js ,然后運(yùn)行 npm install -g vsce 安裝 vsce见秤。在插件的根目錄下運(yùn)行 vsce package 打包插件砂竖,運(yùn)行 vsce publish 發(fā)布插件。

npm install -g vsce
vsce create-publisher poetry # 這一步先創(chuàng)建一個(gè)發(fā)布賬號(hào)鹃答,需要用到token乎澄,看下面步驟獲取token
vsce package #打包插件 .vsix 格式
vsce publish #發(fā)布到 MarketPlace

發(fā)布插件到 VS Code 插件市場,需要注冊開發(fā)者賬號(hào)测摔。有關(guān)如何發(fā)布插件的內(nèi)容很簡單置济,可以參考官方文檔這一部分的內(nèi)容:https://code.visualstudio.com/api/working-with-extensions/publishing-extension

  • 在Visual Studio Team Services 創(chuàng)建一個(gè)賬號(hào)

  • 根據(jù)賬號(hào)的名字訪問主頁解恰,例如我的名字是bingou-ms,主頁鏈接就是

  • 創(chuàng)建Personal Access Token

image

image

需要將Accounts設(shè)置為All accessible accounts

image

那么如何升級(jí)已經(jīng)發(fā)布到插件市場的插件呢浙于?修改/增加版本號(hào)护盈,然后再執(zhí)行 vsce publish 即可。

安裝vsix文件

可以直接安裝vsce package的vsix文件羞酗,方便在本地進(jìn)行調(diào)試腐宋。

code --install-extension vsix文件名

參考文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市整慎,隨后出現(xiàn)的幾起案子脏款,更是在濱河造成了極大的恐慌,老刑警劉巖裤园,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤师,死亡現(xiàn)場離奇詭異,居然都是意外死亡拧揽,警方通過查閱死者的電腦和手機(jī)剃盾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淤袜,“玉大人痒谴,你說我怎么就攤上這事≌∠郏” “怎么了积蔚?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長烦周。 經(jīng)常有香客問我尽爆,道長,這世上最難降的妖魔是什么读慎? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任漱贱,我火速辦了婚禮,結(jié)果婚禮上夭委,老公的妹妹穿的比我還像新娘幅狮。我一直安慰自己,他們只是感情好株灸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布崇摄。 她就那樣靜靜地躺著,像睡著了一般慌烧。 火紅的嫁衣襯著肌膚如雪配猫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天杏死,我揣著相機(jī)與錄音,去河邊找鬼。 笑死淑翼,一個(gè)胖子當(dāng)著我的面吹牛腐巢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播玄括,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼冯丙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了遭京?” 一聲冷哼從身側(cè)響起胃惜,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哪雕,沒想到半個(gè)月后船殉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斯嚎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年利虫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堡僻。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡糠惫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钉疫,到底是詐尸還是另有隱情硼讽,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布牲阁,位于F島的核電站固阁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咨油。R本人自食惡果不足惜您炉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望役电。 院中可真熱鬧赚爵,春花似錦、人聲如沸法瑟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霎挟。三九已至窝剖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酥夭,已是汗流浹背赐纱。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工脊奋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疙描。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓诚隙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親起胰。 傳聞我的和親對象是個(gè)殘疾皇子久又,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容