前一篇文章简单的介绍了一下 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 的逻辑。