从theia的扩展机制讲起
theia所有插件扩展的方法都是以DI的方式进行管理和扩展,在运行时进行依赖注入的方式,在Javascript代码编码的过程中,主要是以inversifyJS这一扩展库管理依赖;
theia的扩展开发工程主要分为两类,包含IDE工程和插件工程,插件工程被IDE工程依赖,只要用于定义扩展能力,比如使用theia和vscode的扩展定进行内容功能的定制和定义;而IDE工程主要用于声明被编译的IDE(集成开发环境包含哪些插件以及运行时容器的一些信息的配置)。
IDE工程的配置方法
对于一个IDE工程而言,几乎不包含任何的源代码信息,主要是一份基于package.json规范的配置文件定义当前的IDE的元数据信息和所包含的插件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| { "private": true, "name": "@idev/example-browser", "version": "1.11.0", "license": "", "theia": { "frontend": { "config": { "applicationName": "Theia IDE Example", "preferences": { "files.enableTrash": false } } } }, "dependencies": { "@idev/framework": "1.0.0", }, "scripts": { "prepare": "yarn run clean && yarn build", "clean": "theia clean", "build": "theia build --mode development", "watch": "yarn build --watch", "start": "theia start --plugins=local-dir:../../plugins", }, "devDependencies": { "@theia/cli": "1.11.0" } }
|
如上所示的一份配置文件就定义了一个基于默认@idev/framework的IDE工具的配置,其余比如name等的配置方法保持和npm包配置规范相一致。
这里需要注意的是,对于IDE工具和相关插件的开发都是以yarn作为包管理的工具(由于yarn对workspace的支持,可以在开发阶段完成更加复杂的分模块隔离研发,提高分工协作和研发管理的效率)。
插件工程的配置方法
对于插件工程,也是一份标准的npm包,其中比较核心的信息都实在package.json中进行定义,如下展示了一个基本的theia开发工程的配置文件信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| { "private": true, "name": "@idev/framework", "version": "1.0.0", "description": "iDev", "dependencies": { "@theia/core": "1.11.0", "@theia/output": "1.11.0", "@theia/vsx-registry": "1.11.0" }, "theiaExtensions": [ { "frontend": "lib/browser/frontend-module" } ], "keywords": [ "idev-extension" ], "files": [ "lib", "src" ], "scripts": { "lint": "theiaext lint", "build": "theiaext build", "watch": "theiaext watch", "clean": "theiaext clean" }, "devDependencies": { "@theia/ext-scripts": "1.11.0" } }
|
其中lib/browser/frontend-module代表了用于扩展插件能力的源代码的入口(这个入口并不是源代码文件的入口,而是源代码ts编译为js后js文件所在的文件路径)。
theia里有针对不同场景的扩展点的支持,开发者需要根据自己的研发环境和任务目标选择不同的适合实际研发行为的扩展点进行功能开发。
对于任一的扩展文件入口,最终export的逻辑都是inversify的ContainerModule。
1 2
| export default new ContainerModule(bind => { });
|
左侧边栏容器扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| export default new ContainerModule(bind => { bindViewContribution(bind, PluginViewContribution); bind(WidgetFactory).toDynamicValue(ctx => ({ id: MainViewContainer.ID, createWidget: async () => { const child = ctx.container.createChild(); child.bind(ViewContainerIdentifier).toConstantValue({ id: MainViewContainer.ID, progressLocationId: 'extensions' }); child.bind(MainViewContainer).toSelf(); const viewContainer = child.get(MainViewContainer); return viewContainer; } })).inSingletonScope(); bind(FrontendApplicationContribution).toService(PluginViewContribution); });
|
bindViewContribution是核心框架提供出来的API,用于定义侧边栏扩展,类型PluginViewContributio其实是AbstractViewContribution的子类,是用于侧边栏扩展的一个工具类,可以方便的在任一的侧边栏位置扩展定制;
WidgetFactory是UI类插件的控件工厂,可以以开发者可以控制的方式为插件生产控件,如上案例代码中就是用于生产一个侧边栏内容位置的工厂方法。
FrontendApplicationContribution是插件扩展的核心扩展点,其中包含菜单,命令的扩展方法,以上实例通过依赖注入的方式将PluginViewContribution定义为FrontendApplicationContribution。