大家好,我是韋恩,今天我們來演練一下如何在vscode裡面跟使用者做對答互動,使用GUI互動的方式滿足實務上的需求與功能。
想像一下,我們正在terminal裡面,正想要使用操作git完成以下幾件事情:
branch
,列出所有當前的branch。$ git branch
$ git checkout -b newBranch
$ git branch
我們要在terminal裡面輸入這幾個指令。
現在,我們想讓使用者可以在VSCode裡面舒服的完成這幾件事情,我們的Extension可以這樣做,幫助使用者更好地完成工作。
在vscode裡面的status bar的ui上創建一個item呈現當前branch的狀況
在使用者點擊status item的時候,跳出可創建的選項跟其他操作(此處我們點選Create new branch)
跳出輸入框讓使用者輸入branch name
使用者輸入branch名稱後按enter,幫使用者創建一個branch並checkout到新的branch上。
刷新status item的狀態至新的branch上。
以上是一個真實extension使用與互動的場景。VSCode提供的Git Extension的功能已經實現了這個功能。
現在,讓我們來試著實現一下這個feature,在練習過程中了解熟悉這一系列api的使用方式吧!
首先,讓我們了解下如何在statusbar上註冊item,並呈現文字。先讓我們看一下這個範例
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
let item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10000);
item.text = 'day10-master';
item.show();
context.subscriptions.push(item);
}
上面這段程式中,我們使用createStatusBarItem這個api,在使用任何api之前,先讓我們透過自動補全了解一下這個api要怎麼使用。
當我們要create一個StatusBarItem。我們可以傳給create方法StatusBarAlignment
這個enum的left或right值,告訴vscode這個item要放置於左邊還是右邊,並且可以設定優先度數值決定順序,這裡我們設數字10000,我們的statusItem的優先度比所有item更高,好讓item置於最左邊。
之後,我們設定text為'day10-master',再用item底下的show方法顯示item。
此時按F5
,執行結果如下:
已經可以顯示資料了,下一步,我們要讓使用者可以在點擊item時觸發事件。statusbarItem允許我們綁定一個註冊好的Command的Id,在點擊時即會執行這個命令。因此我們註冊一個day10-userinput-api.updateItem
的command,並在command執行後自動刷新item的狀態資料文字,如下所示:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const updateCommandId = 'day10-userinput-api.updateItem';
let item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10000);
item.text = 'day10-master';
item.command = updateCommandId;
item.show();
let updateItemCommand = vscode.commands.registerCommand(updateCommandId, () => {
item.text = 'day10-updatedItemText';
});
context.subscriptions.push(item, updateItemCommand);
}
現在再按F5
並且點擊我們設定好的statusBarItem,我們可以看到已經刷新文字為day10-updatedItemText
了。
現在我們要改為在使用者點擊item時展開下拉選單,選單裡要展示git的branch名字,為此,我們設計了一個MockGit
的class模擬git的操作與branch切換,程式碼如下:
class MockGit {
private head = 0;
private branchs = ['day10-master'];
public get currentBranch() {
return this.branchs[this.head];
}
public checkout(branchName: string) {
this.head = this.branchs.findIndex(n => n === branchName);
return this.currentBranch;
}
public branch(newBranch?: string) {
if(newBranch) {
this.branchs.push(newBranch);
}
return this.branchs;
}
}
有了git後,我們再來看一下如何創建下拉選單,
在vscode,我們可以使用showQuickPick
這個api方法,
透過自動補全,我們可以了解這個showQuickPick會回傳一個thenable,也就是一個可像promismㄧ樣供操作的物件。當user點擊下拉選單,我們即會取得像promise一樣resolve後的回傳值,這裡我們會使用await來取得user輸入的選項。
再來,我們將其封裝為一個可以重複使用的方法,方便在不同的地方使用。
const dropdown = async (dropdownItems: string[]) => await vscode.window.showQuickPick(dropdownItems, {
canPickMany: false,
placeHolder: `Select your git action`,
});
好,現在我們已經有了下拉選單的創建方法,讓我們註冊點擊item後的命令,顯示下拉選單以及git的branch選項吧:
export function activate(context: vscode.ExtensionContext) {
const updateCommandId = 'day10-userinput-api.updateItem';
const git = new MockGit();
let item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10000);
item.text = git.currentBranch;
item.command = updateCommandId;
item.show();
let updateItemCommand = vscode.commands.registerCommand(updateCommandId, async () => {
const answer = await dropdown([
'Create new branch',
...git.branch()
]) || '';
vscode.window.showInformationMessage(answer);
});
context.subscriptions.push(item, updateItemCommand);
}
已經完成了下拉選單,現在我們執行extension,並點擊statusItem,會看到展示的選單!
顯示了下拉選單,再讓我們點擊第一個create branch按鈕吧,看看我們會取得什麼樣的執行結果。
我們會取得user點擊下拉選單的選項,如下所示:
前面我們提供了使用者兩個類型的選項,一種是Create new branch
,另一種則是當前已經有的branch
名稱。
現在我們就根據這兩類結果來做不同處理。
針對第一種,我們會使用VSCode的showInputBox
方法創造一個input給使用者輸入,並設定placeHolder的文字,如下所示:
const input = async (placeHolder: string) => await vscode.window.showInputBox({
placeHolder
}) || '';
有了Input之後,我們就可以使用它取得user想新增的branch名稱,相關程式碼如下:
let updateItemCommand = vscode.commands.registerCommand(updateCommandId, async () => {
const answer = await dropdown([ 'Create new branch', ...git.branch()]) || '';
if(answer === 'Create new branch') {
const newBranchName = await input('New Branch Name');
git.branch(newBranchName);
item.text = git.checkout(newBranchName);
vscode.window.showInformationMessage(`Create new branch ${newBranchName}`);
} else if(answer !== '') {
...
}
}
完成上面邏輯後,我們在else if分支這邊裡撰寫checkout選中branch的功能,這裡有個小細節請讀者留意,我們需在answer的字串不為空的狀況才執行程式碼。因為Inputbox可能會遇到使用者點到其他vscode ui跳掉,這時根據我們前面的邏輯,answer會自動拿到空值,導致之後的邏輯設成item的文字為空,這時候在使用者看起來就像是item消失一樣。Checkout branch時的處理邏輯如下:
...
} else if (answer !=='') {
item.text = git.checkout(answer);
vscode.window.showInformationMessage(`Checkout to branch ${git.currentBranch}`);
}
到這裡為止,整個git的處理流程與邏輯均已完成,讓我們執行看看整個操作流程吧!
到這裡為止,我們完成了創建新的branch、切換選擇的branch這兩種功能,現在如果我們要讓使用者可以刪除這個branch,應該要怎麼完成他們呢? 這個問題我們會留給讀者練習並嘗試解答,希望讀者試著自己思考後寫下程式碼後並完成功能喔,答案我們之後會揭曉。
好的,今天的練習到這裡為止!我們成功模擬了一個使用者互動情境,並取得用戶輸入結果,大家是不是對vscode的核心api越來越有感覺了呢?在今天的練習裡,我們運用到的JS的Class與非同步async/await來處理現實當中extension需求,讀者們的js/ts基礎越穩固,對於這章的理解也會越好。如有問題,請在文章下方留言給我,我會針對您的問題幫忙解惑。
明天,會有更多的介紹,我們明天見,掰掰!