乐趣区

认识vscode二

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

退出移动版