xmake-vscode插件開發過程記錄
最近打算給xmake寫一些IDE和編輯器的集成插件,發現vscode的編輯器插件比較容易上手的,就先研究了下vscode的插件開發流程,並且完成了xmake-vscode插件的開發。
我們先來看幾張最後的效果圖:
語法高亮和自動補全
狀態欄
要實現上面的效果,其實並不複雜,首先我們先來簡單介紹下,vscode的插件開發的基本流程:
安裝插件開發環境
安裝cnpm
由於國內環境比較複雜,直接用npm安裝也許很慢或者訪問不穩定,因此這裡先安裝了cnpm去默認使用淘寶的鏡像源。
$ npm install -g cnpm --registry=https://registry.npm.taobao.orgn
創建空工程
通過cnpm去安裝yo工具,用來創建一個vscode插件的空工程
$ cnpm install -g yo generator-coden$ yo coden
大體的源碼結構如下:
選擇創建項目後有四個輸入和一個選擇:
- 輸入你擴展的名稱 xmake-vscode
- 輸入一個標誌(項目創建的文件名稱用這個)xmake-vscode
- 輸入對這個擴展的描述
- 輸入以後要發布用到的一名稱(和以後再發布時候有一個名字是對應上的)tboox
- 是問你要不要創建一個git倉庫用於版本管理
創建完成後的空工程,我們可以用vscode直接打開,然後進行調試載入運行下:
載入起來後,敲F1打開命令窗口,運行默認的hello world測試命令:
到此,一個簡答的demo插件就搞定了,接下來我們簡單介紹下如何發布這個插件到vscode的market上去。
創建發布者
首先我們需要在marketplace.visualstudio.com上註冊一個賬號,創建一個發布者,這裡我取名為tboox
然後,我們需要在自己的賬號裡面,添加一個Personal Access Token(地址:https://[your name].http://visualstudio.com/_details/security/tokens,注意Token只顯示一次,最好自己保存一份)
接著,我們安裝下vsce這個工具,用於vscode的插件工程打包編譯和發布。
$ cnpm install -g vscen
安裝好vsce後,我們先創建一個發布者,這裡為tboox,輸入剛剛market賬號裡面提供的token進行綁定。
$ vsce create-publisher (publisher name)n
構建發布
最後,只需要通過下面命令進行打包或者發布就行了,如果僅僅打個本地包,拖入vscode載入測試,可以運行:
$ vsce packagen
這將會生成一個類似xmake-vscode-0.0.1.vslx的插件包文件,用vscode可直接載入運行。
如果,我們已經開發完了插件,想要發布到market市場,可以執行:
$ vsce publish [version]n
這個時候,我們就可以在xmake-vscode on marketplace上看到你的插件了,用戶也可以直接通過vscode進行搜索和安裝使用。
插件開發詳解
插件的載入機制
插件通過工程根目錄extension.json中配置的activationEvents進行觸發,例如:
{n "activationEvents": [n "workspaceContains:xmake.lua",n "onCommand:xmake.sayHello"n ]n}n
當vscode打開帶有xmake.lua的目錄或者執行http://xmake.XXX相關命令的時候,都會觸發載入xmake-vscode插件,然後調用src/extension.ts中的activate入口函數,進行插件的載入和初始化。
export function activate(context: vscode.ExtensionContext) {nn let disposable = vscode.commands.registerCommand(xmake.sayHello, () => {n vscode.window.showInformationMessage(Hello XMake!);n });nn context.subscriptions.push(disposable);n}n
上述代碼,在載入插件的時候,註冊sayHello命令,去顯示Hello XMake!提示信息。
創建自定義輸出
vscode通過創建OutputChannel來輸出自己的日誌信息,代碼如下:
import * as vscode from vscode;nnlet log = vscode.window.createOutputChannel("xmake/log");nlog.show();nlog.appendLine("hello xmake!");n
在創建的時候可以指定一個label名,用於區分不同的輸出通道,最後顯示的結果如下:
需要注意的是,必須執行log.show(),輸出才會被顯示出來,並且輸出行為是帶緩存刷新的,並不會實時輸出,也不支持色彩高亮輸出。
創建和控制終端
之前,xmake-vscode就是採用channel的方式來輸出xmake的構建信息,效果不是很理想,因此後來改用了終端直接執行的方式,可以看下下面的效果圖:
那如何控制終端,執行自己的命令呢,其實也非常簡單:
let terminal = vscode.window.createTerminal({name: "xmake"});nterminal.show(true);nterminal.sendText("xmake");n
上面的代碼,通過創建一個label名為xmake的獨立終端,然後發送執行命令:xmake,去讓終端執行xmake進行項目的構建,當然如果要顯示出來,還是要先調用下terminal.show(true)。
添加和讀取全局配置
xmake-vscode裡面增加了一些全局vscode配置項,用於控制xmake-vscode插件的行為,配置清單是在package.json文件中進行描述的,例如:
{n "configuration": {n "type": "object",n "title": "XMake configuration",n "properties": {n "xmake.logLevel": {n "type": "string",n "default": "normal",n "description": "The Log Level: normal/verbose/minimal",n "enum": [n "verbose",n "normal",n "minimal"n ]n },n "xmake.buildDirectory": {n "type": "string",n "default": "${workspaceRoot}/build",n "description": "The Build Output Directory"n },n "xmake.androidNDKDirectory": {n "type": "string",n "default": "",n "description": "The Android NDK Directory"n }n }n }n}n
上述配置,增加了三個配置項,都在xmake.域下面,可在vscode配置中直接搜索xmake相關字樣就能方便找到。
讀取配置也很方便,只要獲取xmake相關域配置,進行讀取就行了:
const config = vscode.workspace.getConfiguration(xmake);nconfig.get("buildDirectory");n
創建狀態欄
狀態欄上的按鈕是可以響應之前創建的那些命令的,例如:xmake.sayHello等,下面我們在狀態欄上創建一個debug按鈕,用來調試運行xmake構建的程序:
let debugButton = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 4.5);nndebugButton.command = xmake.onDebug;ndebugButton.text = `$(bug)`;ndebugButton.tooltip = "Debug the given target";ndebugButton.show();n
createStatusBarItem中第二個參數4.5用於控制按鈕在狀態欄上的布局順序,創建好後,再設置下一些基礎屬性就行了,這裡按鈕的文本直接通過$(bug)設置了一個圖標來顯示,更加的直觀。
更多vscode內置支持的圖標,可以自己從octicons上面去找。
點擊這個按鈕,將會觸發xmake.onDebug命令,然後在終端上執行xmake run -d命令,去運行調試程序。
添加選項輸入列表
在xmake-vscode的狀態欄上,我們還增加了幾個快速配置的狀態按鈕,用於快速切換不同的平台、架構、編譯模式,例如:
這個時候,需要有個選項選擇列表的支持,在點擊按鈕後,列出可以選擇的幾個選項,然後選擇切換,那如何創建這個選項列表呢,直接上代碼:
// 初始化選項列表清單nlet items: vscode.QuickPickItem[] = [];nitems.push({label: "linux", description: "The Linux Platform"});nitems.push({label: "macosx", description: "The MacOS Platform"});nitems.push({label: "windows", description: "The Windows Platform"});nitems.push({label: "android", description: "The Android Platform"});nitems.push({label: "iphoneos", description: "The iPhoneOS Platform"});nitems.push({label: "watchos", description: "The WatchOS Platform"});nitems.push({label: "mingw", description: "The MingW Platform"});nitems.push({label: "cross", description: "The Cross Platform"});nn// 顯示選項列表,提示用戶選擇nconst chosen: vscode.QuickPickItem|undefined = await vscode.window.showQuickPick(items);nif (chosen) {nn // 獲取選擇後的結果,然後更新狀態欄按鈕文本n platButton.text = chosen.label;n}n
自定義語法高亮
語法高亮完全可以通過配置文件來搞定,不用寫代碼,當然也可以在代碼中動態配置,這樣稍微繁瑣些。
xmake-vscode裡面需要處理工程xmake.lua描述文件的語法高亮,因此這邊在package.json裡面先定義了一個叫xmake的語言類型,如果編輯器打開xmake.lua文件,就會對其進行語法高亮處理。
{n "contributes": {n "languages": [n {n "id": "xmake",n "filenames": [n "xmake.lua"n ],n "aliases": [n "XMake"n ],n "configuration": "./languages/xmake-configuration.json"n }n ],n "grammars": [n {n "language": "xmake",n "scopeName": "source.xmake",n "path": "./languages/xmake-grammars.json"n }n ]n }n}n
跟語法高亮相關的描述,都放置在/languages/xmake-grammars.json中,用json來描述,我們也可以用xml的格式來描述,但是這樣可讀性不是很好。
xmake-grammars.json中的描述規則,我們摘錄自lua的grammars文件,因為xmake.lua本身就是基於lua語法的,例如,我們匹配xxx單引號字元串的規則,進行字元串的高亮輸出。
{n "begin": "",n "beginCaptures": {n "0": {n "name": "punctuation.definition.string.begin.xmake"n }n },n "end": "",n "endCaptures": {n "0": {n "name": "punctuation.definition.string.end.xmake"n }n },n "name": "string.quoted.single.xmake",n "patterns": [n {n "include": "#escaped_char"n }n ]n}n
自動補全的實現
代碼的自動提示和補全比較麻煩下,需要寫個自定義的class,通過languages進行註冊:
vscode.languages.registerCompletionItemProvider("xmake", new Completion());n
這裡我們定義了一個Completion類,註冊到xmake語言上去,xmake語言定義,就是剛才講的在package.json中的配置。
然後我們實現下這個Completion類:
export class Completion implements vscode.CompletionItemProvider {nn // 匹配當前輸入,提供需要補全的候選文本列表n public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable<vscode.CompletionItem[]> {nn // 獲取當前輸入的單詞文本n let wordAtPosition = document.getWordRangeAtPosition(position);n var currentWord = ;n if (wordAtPosition && wordAtPosition.start.character < position.character) {n var word = document.getText(wordAtPosition);n currentWord = word.substr(0, position.character - wordAtPosition.start.character);n }nn // 猜測匹配結果,返回候選列表n return new Promise(function (resolve, reject) {n Promise.all([n getLuaKeywordsSuggestions(currentWord),n getXMakeCommandsSuggestions(currentWord)n ]).then(function (results) {n var suggestions = Array.prototype.concat.apply([], results);n resolve(suggestions);n }).catch(err => { reject(err); });n });n }nn // 這裡可以對剛剛返回的候選文本列表在做二次處理,例如:增加詳細的文檔描述信息n public resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken): Thenable<vscode.CompletionItem> {nn // 對每個候選文本增加文檔描述n return new Promise(function (resolve, reject) { n item.documentation = "xxxxxxxxxxx";n resolve(item);n });n }n}n
這部分代碼比較多,就不完全貼出來了,完整實現,可參考:completion.ts
結語
本文講述的一些vscode插件代碼都來自xmake-vscode,有興趣的同學可以直接參考源碼,寫個自己的插件。
原文出處:tboox.org/cn/2017/10/…
推薦閱讀:
※Lua 語言有哪些不足?
※用好Lua+Unity,讓性能飛起來—LuaJIT性能坑詳解
※Lua性能優化—Lua內存優化
※從零開始製作2048遊戲
※Lua程序逆向之Luac文件格式分析