xmake-vscode外掛開發過程記錄

ruki發表於2017-10-12

最近打算給xmake寫一些IDE和編輯器的整合外掛,發現vscode的編輯器外掛比較容易上手的,就先研究了下vscode的外掛開發流程,並且完成了xmake-vscode外掛的開發。

我們先來看幾張最後的效果圖:

語法高亮和自動補全

xmake-vscode外掛開發過程記錄

狀態列

statusbar
statusbar

要實現上面的效果,其實並不複雜,首先我們先來簡單介紹下,vscode的外掛開發的基本流程:

安裝外掛開發環境

安裝cnpm

由於國內環境比較複雜,直接用npm安裝也許很慢或者訪問不穩定,因此這裡先安裝了cnpm去預設使用淘寶的映象源。

$ npm install -g cnpm --registry=https://registry.npm.taobao.org複製程式碼

建立空工程

通過cnpm去安裝yo工具,用來建立一個vscode外掛的空工程

$ cnpm install -g yo generator-code
$ yo code複製程式碼

大體的原始碼結構如下:

xmake-vscode外掛開發過程記錄

選擇建立專案後有四個輸入和一個選擇:

  • 輸入你擴充套件的名稱 xmake-vscode
  • 輸入一個標誌(專案建立的檔名稱用這個)xmake-vscode
  • 輸入對這個擴充套件的描述
  • 輸入以後要釋出用到的一名稱(和以後再發布時候有一個名字是對應上的)tboox
  • 是問你要不要建立一個git倉庫用於版本管理

建立完成後的空工程,我們可以用vscode直接開啟,然後進行除錯載入執行下:

xmake-vscode外掛開發過程記錄

載入起來後,敲F1開啟命令視窗,執行預設的hello world測試命令:

xmake-vscode外掛開發過程記錄
xmake-vscode外掛開發過程記錄

到此,一個簡答的demo外掛就搞定了,接下來我們簡單介紹下如何釋出這個外掛到vscode的market上去。

建立釋出者

首先我們需要在marketplace.visualstudio.com上註冊一個賬號,建立一個釋出者,這裡我取名為tboox

然後,我們需要在自己的賬號裡面,新增一個Personal Access Token(地址:https://[your name].visualstudio.com/_details/security/tokens,注意Token只顯示一次,最好自己儲存一份)

接著,我們安裝下vsce這個工具,用於vscode的外掛工程打包編譯和釋出。

$ cnpm install -g vsce複製程式碼

安裝好vsce後,我們先建立一個釋出者,這裡為tboox,輸入剛剛market賬號裡面提供的token進行繫結。

$ vsce create-publisher (publisher name)複製程式碼

構建釋出

最後,只需要通過下面命令進行打包或者釋出就行了,如果僅僅打個本地包,拖入vscode載入測試,可以執行:

$ vsce package複製程式碼

這將會生成一個類似xmake-vscode-0.0.1.vslx的外掛包檔案,用vscode可直接載入執行。

如果,我們已經開發完了外掛,想要釋出到market市場,可以執行:

$ vsce publish [version]複製程式碼

這個時候,我們就可以在xmake-vscode on marketplace上看到你的外掛了,使用者也可以直接通過vscode進行搜尋和安裝使用。

xmake-vscode外掛開發過程記錄

外掛開發詳解

外掛的載入機制

外掛通過工程根目錄extension.json中配置的activationEvents進行觸發,例如:

{
    "activationEvents": [
        "workspaceContains:xmake.lua",
        "onCommand:xmake.sayHello"
    ]
}複製程式碼

當vscode開啟帶有xmake.lua的目錄或者執行xmake.XXX相關命令的時候,都會觸發載入xmake-vscode外掛,然後呼叫src/extension.ts中的activate入口函式,進行外掛的載入和初始化。

export function activate(context: vscode.ExtensionContext) {

    let disposable = vscode.commands.registerCommand('xmake.sayHello', () => {
        vscode.window.showInformationMessage('Hello XMake!');
    });

    context.subscriptions.push(disposable);
}複製程式碼

上述程式碼,在載入外掛的時候,註冊sayHello命令,去顯示Hello XMake!提示資訊。

建立自定義輸出

vscode通過建立OutputChannel來輸出自己的日誌資訊,程式碼如下:

import * as vscode from 'vscode';

let log = vscode.window.createOutputChannel("xmake/log");
log.show();
log.appendLine("hello xmake!");複製程式碼

在建立的時候可以指定一個label名,用於區分不同的輸出通道,最後顯示的結果如下:

xmake-vscode外掛開發過程記錄

需要注意的是,必須執行log.show(),輸出才會被顯示出來,並且輸出行為是帶快取重新整理的,並不會實時輸出,也不支援色彩高亮輸出。

建立和控制終端

之前,xmake-vscode就是採用channel的方式來輸出xmake的構建資訊,效果不是很理想,因此後來改用了終端直接執行的方式,可以看下下面的效果圖:

xmake-vscode外掛開發過程記錄

那如何控制終端,執行自己的命令呢,其實也非常簡單:

let terminal = vscode.window.createTerminal({name: "xmake"});
terminal.show(true);
terminal.sendText("xmake");複製程式碼

上面的程式碼,通過建立一個label名為xmake的獨立終端,然後傳送執行命令:xmake,去讓終端執行xmake進行專案的構建,當然如果要顯示出來,還是要先呼叫下terminal.show(true)

新增和讀取全域性配置

xmake-vscode裡面增加了一些全域性vscode配置項,用於控制xmake-vscode外掛的行為,配置清單是在package.json檔案中進行描述的,例如:

{
    "configuration": {
        "type": "object",
        "title": "XMake configuration",
        "properties": {
            "xmake.logLevel": {
                "type": "string",
                "default": "normal",
                "description": "The Log Level: normal/verbose/minimal",
                "enum": [
                    "verbose",
                    "normal",
                    "minimal"
                ]
            },
            "xmake.buildDirectory": {
                "type": "string",
                "default": "${workspaceRoot}/build",
                "description": "The Build Output Directory"
            },
            "xmake.androidNDKDirectory": {
                "type": "string",
                "default": "",
                "description": "The Android NDK Directory"
            }
        }
    }
}複製程式碼

上述配置,增加了三個配置項,都在xmake.域下面,可在vscode配置中直接搜尋xmake相關字樣就能方便找到。

xmake-vscode外掛開發過程記錄

讀取配置也很方便,只要獲取xmake相關域配置,進行讀取就行了:

const config = vscode.workspace.getConfiguration('xmake');
config.get("buildDirectory");複製程式碼

建立狀態列

狀態列上的按鈕是可以響應之前建立的那些命令的,例如:xmake.sayHello等,下面我們在狀態列上建立一個debug按鈕,用來除錯執行xmake構建的程式:

let debugButton = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 4.5);

debugButton.command = 'xmake.onDebug';
debugButton.text = `$(bug)`;
debugButton.tooltip = "Debug the given target";
debugButton.show();複製程式碼

createStatusBarItem中第二個引數4.5用於控制按鈕在狀態列上的佈局順序,建立好後,再設定下一些基礎屬性就行了,這裡按鈕的文字直接通過$(bug)設定了一個圖示來顯示,更加的直觀。

更多vscode內建支援的圖示,可以自己從octicons上面去找。

點選這個按鈕,將會觸發xmake.onDebug命令,然後在終端上執行xmake run -d命令,去執行除錯程式。

xmake-vscode外掛開發過程記錄

新增選項輸入列表

xmake-vscode的狀態列上,我們還增加了幾個快速配置的狀態按鈕,用於快速切換不同的平臺、架構、編譯模式,例如:

xmake-vscode外掛開發過程記錄

這個時候,需要有個選項選擇列表的支援,在點選按鈕後,列出可以選擇的幾個選項,然後選擇切換,那如何建立這個選項列表呢,直接上程式碼:


// 初始化選項列表清單
let items: vscode.QuickPickItem[] = [];
items.push({label: "linux", description: "The Linux Platform"});
items.push({label: "macosx", description: "The MacOS Platform"});
items.push({label: "windows", description: "The Windows Platform"});
items.push({label: "android", description: "The Android Platform"});
items.push({label: "iphoneos", description: "The iPhoneOS Platform"});
items.push({label: "watchos", description: "The WatchOS Platform"});
items.push({label: "mingw", description: "The MingW Platform"});
items.push({label: "cross", description: "The Cross Platform"});

// 顯示選項列表,提示使用者選擇
const chosen: vscode.QuickPickItem|undefined = await vscode.window.showQuickPick(items);
if (chosen) {

    // 獲取選擇後的結果,然後更新狀態列按鈕文字
    platButton.text = chosen.label;
}複製程式碼

自定義語法高亮

語法高亮完全可以通過配置檔案來搞定,不用寫程式碼,當然也可以在程式碼中動態配置,這樣稍微繁瑣些。

xmake-vscode裡面需要處理工程xmake.lua描述檔案的語法高亮,因此這邊在package.json裡面先定義了一個叫xmake的語言型別,如果編輯器開啟xmake.lua檔案,就會對其進行語法高亮處理。

{
    "contributes": {
        "languages": [
            {
                "id": "xmake",
                "filenames": [
                    "xmake.lua"
                ],
                "aliases": [
                    "XMake"
                ],
                "configuration": "./languages/xmake-configuration.json"
            }
        ],
        "grammars": [
            {
                "language": "xmake",
                "scopeName": "source.xmake",
                "path": "./languages/xmake-grammars.json"
            }
        ]
    }
}複製程式碼

跟語法高亮相關的描述,都放置在/languages/xmake-grammars.json中,用json來描述,我們也可以用xml的格式來描述,但是這樣可讀性不是很好。

xmake-grammars.json中的描述規則,我們摘錄自lua的grammars檔案,因為xmake.lua本身就是基於lua語法的,例如,我們匹配'xxx'單引號字串的規則,進行字串的高亮輸出。

{
    "begin": "'",
    "beginCaptures": {
        "0": {
            "name": "punctuation.definition.string.begin.xmake"
        }
    },
    "end": "'",
    "endCaptures": {
        "0": {
            "name": "punctuation.definition.string.end.xmake"
        }
    },
    "name": "string.quoted.single.xmake",
    "patterns": [
        {
            "include": "#escaped_char"
        }
    ]
}複製程式碼

自動補全的實現

程式碼的自動提示和補全比較麻煩下,需要寫個自定義的class,通過languages進行註冊:

vscode.languages.registerCompletionItemProvider("xmake", new Completion());複製程式碼

這裡我們定義了一個Completion類,註冊到xmake語言上去,xmake語言定義,就是剛才講的在package.json中的配置。

然後我們實現下這個Completion類:

export class Completion implements vscode.CompletionItemProvider {

    // 匹配當前輸入,提供需要補全的候選文字列表
    public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable<vscode.CompletionItem[]> {

        // 獲取當前輸入的單詞文字
        let wordAtPosition = document.getWordRangeAtPosition(position);
        var currentWord = '';
        if (wordAtPosition && wordAtPosition.start.character < position.character) {
            var word = document.getText(wordAtPosition);
            currentWord = word.substr(0, position.character - wordAtPosition.start.character);
        }

        // 猜測匹配結果,返回候選列表
        return new Promise(function (resolve, reject) {
            Promise.all([
                getLuaKeywordsSuggestions(currentWord),
                getXMakeCommandsSuggestions(currentWord)
            ]).then(function (results) {
                var suggestions = Array.prototype.concat.apply([], results);
                resolve(suggestions);
            }).catch(err => { reject(err); });
        });
    }

    // 這裡可以對剛剛返回的候選文字列表在做二次處理,例如:增加詳細的文件描述資訊
    public resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken): Thenable<vscode.CompletionItem> {

        // 對每個候選文字增加文件描述
        return new Promise(function (resolve, reject) { 
            item.documentation = "xxxxxxxxxxx";
            resolve(item);
         });
    }
}複製程式碼

這部分程式碼比較多,就不完全貼出來了,完整實現,可參考:completion.ts

xmake-vscode外掛開發過程記錄

結語

本文講述的一些vscode外掛程式碼都來自xmake-vscode,有興趣的同學可以直接參考原始碼,寫個自己的外掛。

原文出處:tboox.org/cn/2017/10/…

相關文章