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