前一篇文章简单的介绍了一下vscode源码结构.这次我们来了解一下vscode的运行流程. 下一篇文章我们则切换主题的小案例来深入了解vscode.
首先入口文件是main.js,这个相信大部分人都知道.那么我们来看看main.js的源码.这里相当于运行的是electron的主进程. 这里做了一些初始化操作以后(设置schemes, 初始化一些全局配置比如编辑器工作路径)就调用startup函数来loading vs/code/electron-main/main.ts
function startup(cachedDataDir, nlsConfig) {
nlsConfig._languagePackSupport = true;
process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig);
process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || '';
// Load main in AMD
perf.mark('willLoadMainBundle');
require('./bootstrap-amd').load('vs/code/electron-main/main', () => {
perf.mark('didLoadMainBundle');
});
}
electron-main/main.ts 则会先初始化一些基础服务
private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment] {
const services = new ServiceCollection();
const environmentService = new EnvironmentService(args, process.execPath);
const instanceEnvironment = this.patchEnvironment(environmentService); // Patch `process.env` with the instance's environment
services.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(ILifecycleMainService, new SyncDescriptor(LifecycleMainService));
services.set(IStateService, new SyncDescriptor(StateService));
services.set(IRequestService, new SyncDescriptor(RequestMainService));
services.set(IThemeMainService, new SyncDescriptor(ThemeMainService));
services.set(ISignService, new SyncDescriptor(SignService));
return [new InstantiationService(services, true), instanceEnvironment];
}
然后创建ipcServer把接力棒交给 app.ts
// Startup
await instantiationService.invokeFunction(async accessor => {
const environmentService = accessor.get(IEnvironmentService);
const logService = accessor.get(ILogService);
const lifecycleMainService = accessor.get(ILifecycleMainService);
const configurationService = accessor.get(IConfigurationService);
const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, true);
bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel());
once(lifecycleMainService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose());
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
});
这里我们先了解到instantiationService. 后续我们就经常接触整个服务. 这可以理解成是vscode实现DI的容器. 通过调用instantiationService.createInstance创建的对象, 对象constructor内使用装饰器声明了需要注入的service类型.则instantistionService会自动将已有的service注入
main.ts
return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
app.ts constructor
constructor(
private readonly mainIpcServer: Server,
private readonly userEnv: IProcessEnvironment,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStateService private readonly stateService: IStateService
) {
super();
this.registerListeners();
}
app.ts 在最后会调用window.ts 中的 open 函数来打开一个vscode窗口. 每个window窗口都会打开workbench.html用来渲染我们所看到的整个vscode界面.
private doGetUrl(config: object): string {
return `${require.toUrl('vs/code/electron-browser/workbench/workbench.html')}?config=${encodeURIComponent(JSON.stringify(config))}`;
}
今天的分享就到这里. 明天我们就来通过实现切换主题(dark mode)来深入了解 workbench 的逻辑。