vscode 在标签的src引入别名路径_从零开始 - VSCode 插件运行机制
從加載一個(gè)插件開(kāi)始
以我們熟悉的 vscode-eslint 為例,查看源碼會(huì)發(fā)現(xiàn)入口是 extension.ts 文件里的 activate 函數(shù),它的函數(shù)簽名像這樣:
activate需要了解的一點(diǎn)是, package.json 里的 activationEvents 字段定義了插件的激活事件,考慮到性能問(wèn)題,我們并不需要一啟動(dòng) VSCode 就立即激活所有的插件。activation-events 定義了一組事件,當(dāng) activationEvents 字段指定的事件被觸發(fā)時(shí)才會(huì)激活相應(yīng)的插件。包含了特定語(yǔ)言的文件被打開(kāi),或者特定的【命令】被觸發(fā),以及某些視圖被切換甚至是一些自定義命令被觸發(fā)等等事件。
例如在 vscode-java 中,activationEvents 字段的值為
"activationEvents"其中包含 languageId 為 java 的文件被打開(kāi),以及由該插件自定義的幾個(gè) JDT 語(yǔ)言服務(wù)命令被觸發(fā),和【工作空間】包含 pom.xml/buld.gradle 這些事件。在以上事件被觸發(fā)時(shí)插件將會(huì)被激活。
這段邏輯被定義在 src/vs/workbench/api/node/extHostExtensionService.ts 中
// 由 ExtensionHostProcessManager 調(diào)用并傳入相應(yīng)事件作為參數(shù)其中 ExtensionsActivator 定義在 src/vs/workbench/api/node/extHostExtensionActivator.ts 中
export當(dāng)調(diào)用 activator.activateByEvent 方法時(shí)(既某個(gè)事件被觸發(fā)),activator 會(huì)獲取所有符合該事件的插件并逐一執(zhí)行 extHostExtensionService._activateExtension 方法(也就是 activator.actualActivateExtension) ,中間省去獲取上下文,記錄日志等一通操作后調(diào)用了 extHostExtensionService._callActivateOptional 靜態(tài)方法
/* 省略部分代碼 */至此,插件被成功激活。
插件如何運(yùn)行
再來(lái)看插件的代碼,插件中需要引入一個(gè)叫 vscode 的模塊
import熟悉 TypeScript 的朋友都知道這實(shí)際上只是引入了一個(gè) vscode.d.ts 類(lèi)型聲明文件而已,這個(gè)文件包含了所有插件可用的 API 及類(lèi)型定義。
這些 API 在插件 import 時(shí)就被注入到了插件的運(yùn)行環(huán)境中,它們定義在源碼 src/vs/workbench/api/node/extHost.api.impl.ts 文件 createApiFactory 函數(shù)中,通過(guò) defineAPI 函數(shù)統(tǒng)一被注入到插件運(yùn)行環(huán)境。
function實(shí)際上也很簡(jiǎn)單,這里的 require 已經(jīng)被 Microsoft/vscode-loader 劫持了,所以在插件代碼中所有通過(guò) import (運(yùn)行時(shí)會(huì)被編譯為 require) 引入的模塊都會(huì)經(jīng)過(guò)這里,通過(guò)這種方式將 API 注入到了插件執(zhí)行環(huán)境中。
一般我們查看資源管理器或者進(jìn)程會(huì)發(fā)現(xiàn) VSCode 創(chuàng)建了很多個(gè)子進(jìn)程,且所有插件都在一個(gè)獨(dú)立的 Extension Host 進(jìn)程在運(yùn)行,這是考慮到插件需要在一個(gè)與主線(xiàn)程完全隔離的環(huán)境下運(yùn)行,保證安全性。那么問(wèn)題來(lái)了,我們調(diào)用 vscode.window.setStatusBarMessage('Hello World') 時(shí)是怎么在編輯器狀態(tài)欄插入消息的?前文我們提到所有的 API 被定義在 extHost.api.impl.ts 文件的 createApiFactory 里,例如 vscode.window.setStatusBarMessage 的實(shí)現(xiàn)
const實(shí)際調(diào)用的是 extHostStatusBar.setStatusBarMessage 函數(shù),而 extHostStatusBar 則是 ExtHostStatusBar 的實(shí)例
constExtHostStatusBar 包含了兩個(gè)方法 createStatusBarEntry 和 setStatusBarMessage,createStatusBarEntry 返回了一個(gè) ExtHostStatusBarEntry ,它被包裝了一層代理,在 ExtHostStatusBar 被實(shí)例化化的同時(shí)也會(huì)產(chǎn)生一個(gè) ExtHostStatusBarEntry 實(shí)例
export所以當(dāng)我們調(diào)用 setStatusBarMessage 時(shí),先是調(diào)用了 this._statusMessage.setMessage 方法
// setStatusBarMessage 方法而 this._statusMessage.setMessage 方法經(jīng)過(guò)層層調(diào)用,最終調(diào)用了 ExtHostStatusBarEntry 實(shí)例的 update 方法,也就是前面的 StatusBarMessage 構(gòu)造函數(shù)中的 this._item.update,而這里就到了重頭戲,update 方法中包含了一個(gè) 延時(shí)為 0 的 setTimeout :
this這里的 this.proxy 就是 ExtHostStatusBar 構(gòu)造函數(shù)中的 this.proxy
constructor這里的 IMainContext 其實(shí)就是繼承了 IRPCProtocol 的一個(gè)別名而已,new ExtHostStatusBar 的參數(shù)是一個(gè) rpcProtocol 實(shí)例,它被定義在 src/vs/workbench/services/extensions/node/rpcProtocol.ts 中,我們重點(diǎn)看一下 getProxy 的實(shí)現(xiàn)
// 我錯(cuò)了,這里才是重頭戲,VSCode 源碼太繞了 /(ㄒoㄒ)/~~_createProxy 返回的是一個(gè)代理對(duì)象,即它代理了主線(xiàn)程中真正實(shí)現(xiàn)這些 API 的對(duì)象,例如 'MainThreadStatusBar' 返回的是一個(gè) MainThreadStatusBarShape 類(lèi)型的代理。
export插件 API 定義中并沒(méi)有實(shí)現(xiàn)這個(gè)接口,它只需要被主線(xiàn)程中對(duì)應(yīng)的模塊實(shí)現(xiàn)即可,前面我們說(shuō)到 setStatusMessage 最終調(diào)用了 this._proxy.$setEntry。
_remoteCall 里會(huì)調(diào)用 RPCProcotol 的靜態(tài)方法 serializeRequest 將 rpcId 方法名以及參數(shù)序列化成一個(gè) Buffer 并發(fā)送給主線(xiàn)程。
const關(guān)于主線(xiàn)程中接收到消息如何處理其實(shí)已經(jīng)不用多說(shuō)了,根據(jù) rpcId 找到對(duì)應(yīng)的 Services 以及方法,傳入?yún)?shù)即可。
在寫(xiě)這篇文章的同時(shí)也在思考如何在瀏覽器與服務(wù)器端實(shí)現(xiàn)這樣一個(gè)插件加載和運(yùn)行機(jī)制,順便寫(xiě)了一個(gè) Demo extensions-example 相比 VSCode 非常非常簡(jiǎn)單,只是大致模擬了整個(gè)過(guò)程而已,實(shí)際還有很多需要完善的地方,有興趣的可以參考一下。
總結(jié)
以上是生活随笔為你收集整理的vscode 在标签的src引入别名路径_从零开始 - VSCode 插件运行机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 相关方登记册模板_项目的主要相关方
- 下一篇: python和c语言的对比_类C语言与P