微软 VSCode IDE 源码分析揭秘
作者:zanewang,騰訊 CSIG 工程師
目錄
(1)簡介
(2)技術架構
(3)啟動主進程
(4)實例化服務
(5)事件分發
(6)進程通信
(7)主要窗口
(8)開發調試
1.簡介
Visual Studio Code(簡稱 VSCode) 是開源免費的 IDE 編輯器,原本是微軟內部使用的云編輯器(Monaco)。
git 倉庫地址:https://github.com/microsoft/vscode
通過 Eletron 集成了桌面應用,可以跨平臺使用,開發語言主要采用微軟自家的 TypeScript。
整個項目結構比較清晰,方便閱讀代碼理解。成為了最流行跨平臺的桌面 IDE 應用
微軟希望 VSCode 在保持核心輕量級的基礎上,增加項目支持,智能感知,編譯調試。
編譯安裝
下載最新版本,目前我用的是 1.37.1 版本
官方的 wiki 中有編譯安裝的說明 How to Contribute
Linux, Window, MacOS 三個系統編譯時有些差別,參考官方文檔,
在編譯安裝依賴時如果遇到 connect timeout, 需要進行科學上網。
需要注意的一點 運行環境依賴版本 Nodejs x64 version >= 10.16.0, < 11.0.0, python 2.7(3.0 不能正常執行)
2.技術架構
Electron
Electron 是一個使用 JavaScript, HTML 和 CSS 等 Web 技術創建原生程序的框架,它負責比較難搞的部分,你只需把精力放在你的應用的核心上即可 (Electron = Node.js + Chromium + Native API)
Monaco Editor
Monaco Editor是微軟開源項目, 為 VS Code 提供支持的代碼編輯器,運行在瀏覽器環境中。編輯器提供代碼提示,智能建議等功能。供開發人員遠程更方便的編寫代碼,可獨立運行。
TypeScript
TypeScript是一種由微軟開發的自由和開源的編程語言。它是 JavaScript 的一個超集,而且本質上向這個語言添加了可選的靜態類型和基于類的面向對象編程
目錄結構
├──?build?????????#?gulp編譯構建腳本 ├──?extensions????#?內置插件 ├──?product.json??#?App?meta信息 ├──?resources?????#?平臺相關靜態資源 ├──?scripts???????#?工具腳本,開發/測試 ├──?src???????????#?源碼目錄 └──?typings???????#?函數語法補全定義 └──?vs├──?base????????#?通用工具/協議和UI庫│???├──?browser?#?基礎UI組件,DOM操作│???├──?common??#?diff描述,markdown解析器,worker協議,各種工具函數│???├──?node????#?Node工具函數│???├──?parts???#?IPC協議(Electron、Node),quickopen、tree組件│???├──?test????#?base單測用例│???└──?worker??# Worker factory和main Worker(運行IDE Core:Monaco)├──?code????????#?VSCode主運行窗口├──?editor????????#?IDE代碼編輯器|???├──?browser?????#?代碼編輯器核心|???├──?common??????#?代碼編輯器核心|???├──?contrib?????#?vscode?與獨立?IDE共享的代碼|???└──?standalone??#?獨立?IDE?獨有的代碼├──?platform??????#?支持注入服務和平臺相關基礎服務(文件、剪切板、窗體、狀態欄)├──?workbench?????#?工作區UI布局,功能主界面│???├──?api??????????????#│???├──?browser??????????#│???├──?common???????????#│???├──?contrib??????????#│???├──?electron-browser?#│???├──?services?????????#│???└──?test?????????????#├──?css.build.js??#?用于插件構建的CSS?loader├──?css.js????????#?CSS?loader├──?editor????????#?對接IDE?Core(讀取編輯/交互狀態),提供命令、上下文菜單、hover、snippet等支持├──?loader.js?????#?AMD?loader(用于異步加載AMD模塊)├──?nls.build.js??#?用于插件構建的NLS?loader└──?nls.js????????#?NLS(National?Language?Support)多語言loader核心層
base: 提供通用服務和構建用戶界面
platform: 注入服務和基礎服務代碼
editor: 微軟 Monaco 編輯器,也可獨立運行使用
wrokbench: 配合 Monaco 并且給 viewlets 提供框架:如:瀏覽器狀態欄,菜單欄利用 electron 實現桌面程序
核心環境
整個項目完全使用 typescript 實現,electron 中運行主進程和渲染進程,使用的 api 有所不同,所以在 core 中每個目錄組織也是按照使用的 api 來安排,
運行的環境分為幾類:
common: 只使用 javascritp api 的代碼,能在任何環境下運行
browser: 瀏覽器 api, 如操作 dom; 可以調用 common
node: 需要使用 node 的 api,比如文件 io 操作
electron-brower: 渲染進程 api, 可以調用 common, brower, node, 依賴electron renderer-process API
electron-main: 主進程 api, 可以調用: common, node 依賴于electron main-process AP
3.啟動主進程
Electron 通過 package.json 中的 main 字段來定義應用入口。
main.js 是 vscode 的入口。
src/main.js
_ vs/code/electron-main/main.ts
_ vs/code/electron-main/app.ts
_ vs/code/electron-main/windows.ts
_ vs/workbench/electron-browser/desktop.main.ts * vs/workbench/browser/workbench.ts
vs/code/electron-main/main.ts
electron-main/main 是程序真正啟動的入口,進入 main process 初始化流程.
這里主要做了兩件事情:
初始化 Service
啟動主實例
直接看 startup 方法的實現,基礎服務初始化完成后會加載 CodeApplication, mainIpcServer, instanceEnvironment,調用 startup 方法啟動 APP
private?async?startup(args:?ParsedArgs):?Promise<void>?{//spdlog?日志服務const?bufferLogService?=?new?BufferLogService();//?1.?調用?createServicesconst?[instantiationService,?instanceEnvironment]?=?this.createServices(args,?bufferLogService);try?{//?1.1?初始化Service服務await?instantiationService.invokeFunction(async?accessor?=>?{//?基礎服務,包括一些用戶數據,緩存目錄const?environmentService?=?accessor.get(IEnvironmentService);//?配置服務const?configurationService?=?accessor.get(IConfigurationService);//?持久化數據const?stateService?=?accessor.get(IStateService);try?{await?this.initServices(environmentService,?configurationService?as?ConfigurationService,?stateService?as?StateService);}?catch?(error)?{//?拋出錯誤對話框this.handleStartupDataDirError(environmentService,?error);throw?error;}});//?1.2?啟動實例await?instantiationService.invokeFunction(async?accessor?=>?{const?environmentService?=?accessor.get(IEnvironmentService);const?logService?=?accessor.get(ILogService);const?lifecycleService?=?accessor.get(ILifecycleService);const?configurationService?=?accessor.get(IConfigurationService);const?mainIpcServer?=?await?this.doStartup(logService,?environmentService,?lifecycleService,?instantiationService,?true);bufferLogService.logger?=?new?SpdLogService('main',?environmentService.logsPath,?bufferLogService.getLevel());once(lifecycleService.onWillShutdown)(()?=>?(configurationService?as?ConfigurationService).dispose());return?instantiationService.createInstance(CodeApplication,?mainIpcServer,?instanceEnvironment).startup();});}?catch?(error)?{instantiationService.invokeFunction(this.quit,?error);}}Service
這里通過 createService 創建一些基礎的 Service
運行環境服務 EnvironmentService
src/vs/platform/environment/node/environmentService.ts
通過這個服務獲取當前啟動目錄,日志目錄,操作系統信息,配置文件目錄,用戶目錄等。
日志服務 MultiplexLogService
src/vs/platform/log/common/log.ts
默認使用控制臺日志 ConsoleLogMainService
其中包含性能追蹤和釋放信息,日志輸出級別
配置服務 ConfigurationService
src/vs/platform/configuration/node/configurationService.ts
從運行環境服務獲取內容
生命周期服務 LifecycleService
src/vs/platform/lifecycle/common/lifecycleService.ts
監聽事件,electron app 模塊 比如:ready, window-all-closed,before-quit
可以參考官方electron app 文檔
狀態服務 StateService
src/vs/platform/state/node/stateService.ts
通過 FileStorage 讀寫 storage.json 存儲,里記錄一些與程序運行狀態有關的鍵值對
請求服務 RequestService
src/vs/platform/request/browser/requestService.ts
這里使用的是原生 ajax 請求,實現了 request 方法
主題服務 ThemeMainService
src/vs/platform/theme/electron-main/themeMainService.ts
這里只設置背景顏色,通過 getBackgroundColor 方法 IStateService 存儲
簽名服務 SignService
src/vs/platform/sign/node/signService.ts
private?createServices(args:?ParsedArgs,?bufferLogService:?BufferLogService):?[IInstantiationService,?typeof?process.env]?{//服務注冊容器const?services?=?new?ServiceCollection();const?environmentService?=?new?EnvironmentService(args,?process.execPath);const?instanceEnvironment?=?this.patchEnvironment(environmentService);?//?Patch?`process.env`?with?the?instance's?environmentservices.set(IEnvironmentService,?environmentService);const?logService?=?new?MultiplexLogService([new?ConsoleLogMainService(getLogLevel(environmentService)),?bufferLogService]);process.once('exit',?()?=>?logService.dispose());//日志服務services.set(ILogService,?logService);//配置服務services.set(IConfigurationService,?new?ConfigurationService(environmentService.settingsResource));//生命周期services.set(ILifecycleService,?new?SyncDescriptor(LifecycleService));//狀態存儲services.set(IStateService,?new?SyncDescriptor(StateService));//網絡請求services.set(IRequestService,?new?SyncDescriptor(RequestService));//主題設定services.set(IThemeMainService,?new?SyncDescriptor(ThemeMainService));//簽名服務services.set(ISignService,?new?SyncDescriptor(SignService));return?[new?InstantiationService(services,?true),?instanceEnvironment]; }4.實例化服務
SyncDescriptor 負責注冊這些服務,當用到該服務時進程實例化使用
src/vs/platform/instantiation/common/descriptors.ts
export?class?SyncDescriptor<T>?{readonly?ctor:?any;readonly?staticArguments:?any[];readonly?supportsDelayedInstantiation:?boolean;constructor(ctor:?new?(...args:?any[])?=>?T,?staticArguments:?any[]?=?[],?supportsDelayedInstantiation:?boolean?=?false)?{this.ctor?=?ctor;this.staticArguments?=?staticArguments;this.supportsDelayedInstantiation?=?supportsDelayedInstantiation;} }main.ts 中 startup 方法調用 invokeFunction.get 實例化服務
await?instantiationService.invokeFunction(async?accessor?=>?{const?environmentService?=?accessor.get(IEnvironmentService);const?configurationService?=?accessor.get(IConfigurationService);const?stateService?=?accessor.get(IStateService);try?{await?this.initServices(environmentService,?configurationService?as?ConfigurationService,?stateService?as?StateService);}?catch?(error)?{//?Show?a?dialog?for?errors?that?can?be?resolved?by?the?userthis.handleStartupDataDirError(environmentService,?error);throw?error;} });get 方法調用_getOrCreateServiceInstance,這里第一次創建會存入緩存中
下次實例化對象時會優先從緩存中獲取對象。
src/vs/platform/instantiation/common/instantiationService.ts
invokeFunction<R,?TS?extends?any[]?=?[]>(fn:?(accessor:?ServicesAccessor,?...args:?TS)?=>?R,?...args:?TS):?R?{let?_trace?=?Trace.traceInvocation(fn);let?_done?=?false;try?{const?accessor:?ServicesAccessor?=?{get:?<T>(id:?ServiceIdentifier<T>,?isOptional?:?typeof?optional)?=>?{if?(_done)?{throw?illegalState('service?accessor?is?only?valid?during?the?invocation?of?its?target?method');}const?result?=?this._getOrCreateServiceInstance(id,?_trace);if?(!result?&&?isOptional?!==?optional)?{throw?new?Error(`[invokeFunction]?unknown?service?'${id}'`);}return?result;}};return?fn.apply(undefined,?[accessor,?...args]);}?finally?{_done?=?true;_trace.stop();} } private?_getOrCreateServiceInstance<T>(id:?ServiceIdentifier<T>,?_trace:?Trace):?T?{let?thing?=?this._getServiceInstanceOrDescriptor(id);if?(thing?instanceof?SyncDescriptor)?{return?this._createAndCacheServiceInstance(id,?thing,?_trace.branch(id,?true));}?else?{_trace.branch(id,?false);return?thing;} }vs/code/electron-main/app.ts
這里首先觸發 CodeApplication.startup()方法, 在第一個窗口打開 3 秒后成為共享進程,
async?startup():?Promise<void>?{...//?1.?第一個窗口創建共享進程const?sharedProcess?=?this.instantiationService.createInstance(SharedProcess,?machineId,?this.userEnv);const?sharedProcessClient?=?sharedProcess.whenReady().then(()?=>?connect(this.environmentService.sharedIPCHandle,?'main'));this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(()?=>?{this._register(new?RunOnceScheduler(async?()?=>?{const?userEnv?=?await?getShellEnvironment(this.logService,?this.environmentService);sharedProcess.spawn(userEnv);},?3000)).schedule();});//?2.?創建app實例const?appInstantiationService?=?await?this.createServices(machineId,?trueMachineId,?sharedProcess,?sharedProcessClient);//?3.?打開一個窗口?調用const?windows?=?appInstantiationService.invokeFunction(accessor?=>?this.openFirstWindow(accessor,?electronIpcServer,?sharedProcessClient));//?4.?窗口打開后執行生命周期和授權操作this.afterWindowOpen();...//vscode結束了性能問題的追蹤if?(this.environmentService.args.trace)?{this.stopTracingEventually(windows);} }openFirstWindow 主要實現
CodeApplication.openFirstWindow 首次開啟窗口時,創建 Electron 的 IPC,使主進程和渲染進程間通信。
window 會被注冊到 sharedProcessClient,主進程和共享進程通信
根據 environmentService 提供的參數(path,uri)調用 windowsMainService.open 方法打開窗口
vs/code/electron-main/windows.ts
接下來到了 electron 的 windows 窗口,open 方法在 doOpen 中執行窗口配置初始化,最終調用 openInBrowserWindow -> 執行 doOpenInBrowserWindow 是其打開 window,主要步驟如下:
private?openInBrowserWindow(options:?IOpenBrowserWindowOptions):?ICodeWindow?{...//?New?windowif?(!window)?{//1.判斷是否全屏創建窗口...//?2.?創建實例窗口window?=?this.instantiationService.createInstance(CodeWindow,?{state,extensionDevelopmentPath:?configuration.extensionDevelopmentPath,isExtensionTestHost:?!!configuration.extensionTestsPath});//?3.添加到當前窗口控制器WindowsManager.WINDOWS.push(window);//?4.窗口監聽器window.win.webContents.removeAllListeners('devtools-reload-page');?//?remove?built?in?listener?so?we?can?handle?this?on?our?ownwindow.win.webContents.on('devtools-reload-page',?()?=>?this.reload(window!));window.win.webContents.on('crashed',?()?=>?this.onWindowError(window!,?WindowError.CRASHED));window.win.on('unresponsive',?()?=>?this.onWindowError(window!,?WindowError.UNRESPONSIVE));window.win.on('closed',?()?=>?this.onWindowClosed(window!));//?5.注冊窗口生命周期(this.lifecycleService?as?LifecycleService).registerWindow(window);}...return?window; }doOpenInBrowserWindow 會調用 window.load 方法 在 window.ts 中實現
load(config:?IWindowConfiguration,?isReload?:?boolean,?disableExtensions?:?boolean):?void?{...//?Load?URLperf.mark('main:loadWindow');this._win.loadURL(this.getUrl(configuration));... }private?getUrl(windowConfiguration:?IWindowConfiguration):?string?{...//加載歡迎屏幕的htmllet?configUrl?=?this.doGetUrl(config);...return?configUrl; }//默認加載?vs/code/electron-browser/workbench/workbench.html private?doGetUrl(config:?object):?string?{return?`${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; }main process 的使命完成, 主界面進行構建布局。
在 workbench.html 中加載了 workbench.js,
這里調用 return require('vs/workbench/electron-browser/desktop.main').main(configuration);實現對主界面的展示
vs/workbench/electron-browser/desktop.main.ts
創建工作區,調用 workbench.startup()方法,構建主界面展示布局
... async?open():?Promise<void>?{const?services?=?await?this.initServices();await?domContentLoaded();mark('willStartWorkbench');//?1.創建工作區const?workbench?=?new?Workbench(document.body,?services.serviceCollection,?services.logService);//?2.監聽窗口變化this._register(addDisposableListener(window,?EventType.RESIZE,?e?=>?this.onWindowResize(e,?true,?workbench)));//?3.工作臺生命周期this._register(workbench.onShutdown(()?=>?this.dispose()));this._register(workbench.onWillShutdown(event?=>?event.join(services.storageService.close())));//?3.啟動工作區const?instantiationService?=?workbench.startup();... } ...vs/workbench/browser/workbench.ts
工作區繼承自 layout 類,主要作用是構建工作區,創建界面布局。
export?class?Workbench?extends?Layout?{...startup():?IInstantiationService?{try?{...//?Servicesconst?instantiationService?=?this.initServices(this.serviceCollection);instantiationService.invokeFunction(async?accessor?=>?{const?lifecycleService?=?accessor.get(ILifecycleService);const?storageService?=?accessor.get(IStorageService);const?configurationService?=?accessor.get(IConfigurationService);//?Layoutthis.initLayout(accessor);//?Registriesthis.startRegistries(accessor);//?Context?Keysthis._register(instantiationService.createInstance(WorkbenchContextKeysHandler));//?注冊監聽事件this.registerListeners(lifecycleService,?storageService,?configurationService);//?渲染工作區this.renderWorkbench(instantiationService,?accessor.get(INotificationService)?as?NotificationService,?storageService,?configurationService);//?創建工作區布局this.createWorkbenchLayout(instantiationService);//?布局構建this.layout();//?Restoretry?{await?this.restoreWorkbench(accessor.get(IEditorService),?accessor.get(IEditorGroupsService),?accessor.get(IViewletService),?accessor.get(IPanelService),?accessor.get(ILogService),?lifecycleService);}?catch?(error)?{onUnexpectedError(error);}});return?instantiationService;}?catch?(error)?{onUnexpectedError(error);throw?error;?//?rethrow?because?this?is?a?critical?issue?we?cannot?handle?properly?here}}... }5.事件分發
event
src/vs/base/common/event.ts
程序中常見使用 once 方法進行事件綁定, 給定一個事件,返回一個只觸發一次的事件,放在匿名函數返回
export?function?once<T>(event:?Event<T>):?Event<T>?{return?(listener,?thisArgs?=?null,?disposables?)?=>?{//?設置次變量,防止事件重復觸發造成事件污染let?didFire?=?false;let?result:?IDisposable;result?=?event(e?=>?{if?(didFire)?{return;}?else?if?(result)?{result.dispose();}?else?{didFire?=?true;}return?listener.call(thisArgs,?e);},?null,?disposables);if?(didFire)?{result.dispose();}return?result;}; }循環派發了所有注冊的事件, 事件會存儲到一個事件隊列,通過 fire 方法觸發事件
private _deliveryQueue?: LinkedList<[Listener, T]>;//事件存儲隊列
fire(event:?T):?void?{if?(this._listeners)?{//?將所有事件傳入?delivery?queue//?內部/嵌套方式通過emit發出.//?this調用事件驅動if?(!this._deliveryQueue)?{this._deliveryQueue?=?new?LinkedList();}for?(let?iter?=?this._listeners.iterator(),?e?=?iter.next();?!e.done;?e?=?iter.next())?{this._deliveryQueue.push([e.value,?event]);}while?(this._deliveryQueue.size?>?0)?{const?[listener,?event]?=?this._deliveryQueue.shift()!;try?{if?(typeof?listener?===?'function')?{listener.call(undefined,?event);}?else?{listener[0].call(listener[1],?event);}}?catch?(e)?{onUnexpectedError(e);}}} }6.進程通信
主進程
src/vs/code/electron-main/main.ts
main.ts 在啟動應用后就創建了一個主進程 main process,它可以通過 electron 中的一些模塊直接與原生 GUI 交互。
server?=?await?serve(environmentService.mainIPCHandle); once(lifecycleService.onWillShutdown)(()?=>?server.dispose());渲染進程
僅啟動主進程并不能給你的應用創建應用窗口。窗口是通過 main 文件里的主進程調用叫 BrowserWindow 的模塊創建的。
主進程與渲染進程之間的通信
在 electron 中,主進程與渲染進程有很多通信的方法。比如 ipcRenderer 和 ipcMain,還可以在渲染進程使用 remote 模塊。
ipcMain & ipcRenderer
主進程:ipcMain
渲染進程:ipcRenderer
ipcMain 模塊和 ipcRenderer 是類 EventEmitter 的實例。
在主進程中使用 ipcMain 接收渲染線程發送過來的異步或同步消息,發送過來的消息將觸發事件。
在渲染進程中使用 ipcRenderer 向主進程發送同步或異步消息,也可以接收到主進程的消息。
發送消息,事件名為 channel .
回應同步消息, 你可以設置 event.returnValue .
回應異步消息, 你可以使用 event.sender.send(…)
創建 IPC 服務
src/vs/base/parts/ipc/node/ipc.net.ts
這里返回一個 promise 對象,成功則 createServer
export?function?serve(hook:?any):?Promise<Server>?{return?new?Promise<Server>((c,?e)?=>?{const?server?=?createServer();server.on('error',?e);server.listen(hook,?()?=>?{server.removeListener('error',?e);c(new?Server(server));});}); }創建信道
src/vs/code/electron-main/app.ts
mainIpcServer * launchChannel
electronIpcServer
_ updateChannel
_ issueChannel
_ workspacesChannel
_ windowsChannel
_ menubarChannel
_ urlChannel
_ storageChannel
_ logLevelChannel
每一個信道,內部實現兩個方法 listen 和 call
例如:src/vs/platform/localizations/node/localizationsIpc.ts
構造函數綁定事件
export?class?LocalizationsChannel?implements?IServerChannel?{onDidLanguagesChange:?Event<void>;constructor(private?service:?ILocalizationsService)?{this.onDidLanguagesChange?=?Event.buffer(service.onDidLanguagesChange,?true);}listen(_:?unknown,?event:?string):?Event<any>?{switch?(event)?{case?'onDidLanguagesChange':?return?this.onDidLanguagesChange;}throw?new?Error(`Event?not?found:?${event}`);}call(_:?unknown,?command:?string,?arg?:?any):?Promise<any>?{switch?(command)?{case?'getLanguageIds':?return?this.service.getLanguageIds(arg);}throw?new?Error(`Call?not?found:?${command}`);} }7.主要窗口
workbench.ts 中 startup 里面 Workbench 負責創建主界面
src/vs/workbench/browser/workbench.ts
渲染主工作臺,渲染完之后加入到 container 中,container 加入到 parent, parent 就是 body 了。
this.parent.appendChild(this.container);
private?renderWorkbench(instantiationService:?IInstantiationService,?notificationService:?NotificationService,?storageService:?IStorageService,?configurationService:?IConfigurationService):?void?{...//TITLEBAR_PART?頂部操作欄//ACTIVITYBAR_PART?最左側菜單選項卡//SIDEBAR_PART?左側邊欄,顯示文件,結果展示等//EDITOR_PART?右側窗口,代碼編寫,歡迎界面等//STATUSBAR_PART?底部狀態欄[{?id:?Parts.TITLEBAR_PART,?role:?'contentinfo',?classes:?['titlebar']?},{?id:?Parts.ACTIVITYBAR_PART,?role:?'navigation',?classes:?['activitybar',?this.state.sideBar.position?===?Position.LEFT???'left'?:?'right']?},{?id:?Parts.SIDEBAR_PART,?role:?'complementary',?classes:?['sidebar',?this.state.sideBar.position?===?Position.LEFT???'left'?:?'right']?},{?id:?Parts.EDITOR_PART,?role:?'main',?classes:?['editor'],?options:?{?restorePreviousState:?this.state.editor.restoreEditors?}?},{?id:?Parts.PANEL_PART,?role:?'complementary',?classes:?['panel',?this.state.panel.position?===?Position.BOTTOM???'bottom'?:?'right']?},{?id:?Parts.STATUSBAR_PART,?role:?'contentinfo',?classes:?['statusbar']?}].forEach(({?id,?role,?classes,?options?})?=>?{const?partContainer?=?this.createPart(id,?role,?classes);if?(!configurationService.getValue('workbench.useExperimentalGridLayout'))?{//?TODO@Ben?cleanup?once?moved?to?grid//?Insert?all?workbench?parts?at?the?beginning.?Issue?#52531//?This?is?primarily?for?the?title?bar?to?allow?overriding?-webkit-app-regionthis.container.insertBefore(partContainer,?this.container.lastChild);}this.getPart(id).create(partContainer,?options);});//?將工作臺添加至container?dom渲染this.parent.appendChild(this.container);}workbench 最后調用 this.layout()方法,將窗口占據整個界面,渲染完成
layout(options?:?ILayoutOptions):?void?{if?(!this.disposed)?{this._dimension?=?getClientArea(this.parent);if?(this.workbenchGrid?instanceof?Grid)?{position(this.container,?0,?0,?0,?0,?'relative');size(this.container,?this._dimension.width,?this._dimension.height);//?Layout?the?grid?widgetthis.workbenchGrid.layout(this._dimension.width,?this._dimension.height);}?else?{this.workbenchGrid.layout(options);}//?Emit?as?eventthis._onLayout.fire(this._dimension);}}8.開發調試
app.once('ready',?function?()?{//啟動追蹤if?(args['trace'])?{//?@ts-ignoreconst?contentTracing?=?require('electron').contentTracing;const?traceOptions?=?{categoryFilter:?args['trace-category-filter']?||?'*',traceOptions:?args['trace-options']?||?'record-until-full,enable-sampling'};contentTracing.startRecording(traceOptions,?()?=>?onReady());}?else?{onReady();} });啟動追蹤
這里如果傳入 trace 參數,在 onReady 啟動之前會調用 chromium 的收集跟蹤數據,
提供的底層的追蹤工具允許我們深度了解 V8 的解析以及其他時間消耗情況,
一旦收到可以開始記錄的請求,記錄將會立馬啟動并且在子進程是異步記錄聽的. 當所有的子進程都收到 startRecording 請求的時候,callback 將會被調用.
categoryFilter 是一個過濾器,它用來控制那些分類組應該被用來查找.過濾器應當有一個可選的 - 前綴來排除匹配的分類組.不允許同一個列表既是包含又是排斥.
contentTracing.startRecording(options, callback)
options Object
_ categoryFilter String
_ traceOptions Stringcallback Function
關于 trace 的詳細介紹
結束追蹤
contentTracing.stopRecording(resultFilePath, callback)
resultFilePath String
callback Function
在成功啟動窗口后,程序結束性能追蹤,停止對所有子進程的記錄.
子進程通常緩存查找數據,并且僅僅將數據截取和發送給主進程.這有利于在通過 IPC 發送查找數據之前減小查找時的運行開銷,這樣做很有價值.因此,發送查找數據,我們應當異步通知所有子進程來截取任何待查找的數據.
一旦所有子進程接收到了 stopRecording 請求,將調用 callback ,并且返回一個包含查找數據的文件.
如果 resultFilePath 不為空,那么將把查找數據寫入其中,否則寫入一個臨時文件.實際文件路徑如果不為空,則將調用 callback .
debug
調試界面在菜單欄找到 Help->Toggle Developers Tools
調出 Chrome 開發者調試工具進行調試
參考
https://electronjs.org/docs
https://github.com/microsoft/vscode/wiki/How-to-Contribute
https://github.com/Microsoft/vscode/wiki/Code-Organization
http://xzper.com/2016/04/17/vscode%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90/
http://www.ayqy.net/blog/vs-code%E6%BA%90%E7%A0%81%E7%AE%80%E6%9E%90/
推薦閱讀:
下一代 TGW 從13Mpps到50Mpps性能優化之旅
寫給前端工程師的 Flutter 詳細教程
現代化 C++ 開發工具 CLion 從入門到精通
總結
以上是生活随笔為你收集整理的微软 VSCode IDE 源码分析揭秘的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TEG《选择》乘风破浪 · 披荆斩棘
- 下一篇: 深入浅出理解 Spark:环境部署与工作