插件开发
安装环境
在开始插件开发之前,请检查是否已安装 Node.js 16.10.0 或以上版本 (https://nodejs.org/en/)。若已安装,可在本地CMD或CodeArts IDE终端使用命令行 node -v 以及 npm -v 查看相应的安装版本。
插件创建
- 打开 CodeArts IDE,单击菜单“文件 ->新建 ->工程...”,选择“扩展”。
- 类型:“简单扩展”不包含后端模板,“支持可展示的Webview以及弹窗的扩展”以及“支持注册创建项目向导的扩展”包含前端和后端的模板。
- 发布商:发布商必须为插件市场中已创建的发布商,否则将无法在插件市场上发布插件,也可在发布前在已创建扩展工程中的 package.json 文件中修改"publisher"字段。
- 单击“创建”,等待插件工程创建完成,选择是否在当前窗口打开新建的插件工程。
插件调试与运行
后端调试
在插件的 src/node/ 目录下存放的是插件的后端代码,后端代码运行在 nodejs 环境中,插件工程在创建的时候已经默认生成了一个后端文件 backend.ts,对于轻量级的插件,只需要在该文件中添加自己想要实现的业务功能即可,该文件包含了三个默认的方法 init()、run()、stop()。另外还默认添加了一个 doSomething 方法,这个方法仅仅是作为示例使用,开发者可以根据需要进行修改或删除。
这里我们简单介绍下init,run和stop方法:
- init 函数:作为该后端实例的初始化方法,可以在插件启动的时候进行一些初始化操作,写在该函数中的代码一定会先于 run 和其他函数被调用,这里需要注意的是,对于前端暴露给后端的函数不能在 init 函数中进行调用,也就是不能在 init 方法中执行 this.plugin.call 调用。
- run 函数:作为后端实例的主逻辑函数,承担着业务功能入口的作用,在该函数中可以方便地调用 CodeArts IDE 的 API,比如 codearts.window.showInformationMessage(`hello world!`); 也可以调用前端暴露出来的函数,也就是可以在该方法中执行 this.plugin.call 调用。
- stop 函数:将会在插件被停止前被调用,如有需要可以进行一些资源清理的操作。
- 添加断点:在backend.ts 的 run() 函数中添加一个断点。
- 打开调试窗口:按 F5 或者单击右上角调试工具栏中的开始调试按钮,打开【扩展开发宿主】窗口。
- 进入断点,进行调试。
前端调试
与插件的后端不同,前端的代码最终将被编译并运行于浏览器环境中,前端的代码存放于 src/browser 目录中,插件工程在创建的时候会默认生成两个前端源码文件 frontend.ts 和 dynamic-webview.ts。这两个文件的内容与后端 backend.ts 的结构非常相似,只不过运行的环境不同而已,这里就不再重复对这两个文件中 init()、run()、stop() 方法进行介绍。由于前端运行在浏览器环境中,代码调试将借助于浏览器自带的调试功能。如果需要自动重新编译前端代码,可以在终端中执行命令 npm run watch-browser,然后再运行调试。在启动调试后如果修改了代码,只需在调试窗口按 Ctrl+R 重新加载窗口即可看到修改后的效果。
- 前端调试前,需要先把 webpack.config.js 文件中的 devtool 配置为 'inline-source-map',然后在命令行执行 npm run prepare。
- 添加断点:在 frontend.ts 的 run() 函数中添加一个断点。
- 打开调试窗口:按 F5 或者单击右上角调试工具栏中的开始调试按钮,打开【扩展开发宿主】窗口。
- 打开插件注册的视图,进入断点,进行前端的调试,若无法进入断点,可以使用“Ctrl + Shift + I”打开“开发人员工具”,再“Ctrl + R”重新加载当前窗口。
前后端方法相互调用
后端调用前端
- 在前端定义暴露给后端的方法
打开 src/browser/frontend.ts 文件,其中 Frontend 类继承自 AbstractFrontend,除了需要实现的 init()、run()、stop() 这三个方法,我们自定义了一个 myApi(message: string) 方法,如果想要把 myApi 方法暴露给后端去调用,只需要在函数上添加 @expose('function_id') 修饰器。
注意:多个expose修饰器中的function_id不能重复。
@expose('myplugin.page.myApi')public myApi(message: string): string { console.log(message); return 'this is a return value from frontend function'; }
- 在后端调用前端暴露的方法
打开 src/node/backend.ts 文件,其中 Backend 类继承自 AbstractBacend,需要实现 init(), run(), stop() 这三个方法,我们可以在 run() 方法中通过 this.plugin.call() 调用在前端定义的 myApi 方法并获取到返回值。
public async run(): Promise<void> { const retValue = await this.plugin.call('view_type_of_your_plugin_view::myplugin.page.myApi', 'this is a function call from backend'); this.plugin.log(LogLevel.INFO, retValue); }
前端调用后端
类似的,我们可以在后端定义自己的方法并将方法暴露给前端调用。
- 在后端定义暴露给前端的方法
打开 src/node/backend.ts 文件,自定义一个 doSomething(name: string) 方法。
@expose('your_backend_function_identifier')public doSomething(name: string): boolean { codearts.window.showInformationMessage(`hello ${name}!`); return true; }
- 在前端调用后端暴露的方法
打开 src/browser/frontend.ts 文件,在 run() 方法中通过 this.plugin.call() 调用在后端定义的 doSomething 方法。
run(): void { this.plugin.call('your_backend_function_identifier', 'world'); }
事件订阅:发布和监听事件
- 在插件后端监听事件
打开 src/node 目录下的 backend.ts 文件,在 Backend 类的 run() 方法中我们添加如下代码注册监听一个文件删除的事件。
const registeredEvent = codearts.workspace.onDidDeleteFiles((event) => { codearts.window.showInformationMessage(`${event.files.join(',')} deleted.`); }); this.plugin.context.subscriptions.push(registeredEvent);
如果想要删除这个事件的监听可以直接调用 registeredEvent 的 dispose() 方法即可。
大家可以尝试注册一些其他的事件并测试效果。
- 在插件前端监听事件
打开 src/browser 下的 fronted.ts 文件,我们通过在 Frontend 类的 run() 方法中添加如下代码注册监听一个改变当前活动的编辑器的事件。
const eventHandler = (eventType: any, evt: any) => { // do something }; this.plugin.subscribeEvent(EventType.WINDOW_ONDIDCHANGEACTIVETEXTEDITOR, eventHandler);
前端取消事件注册的方式和后端并不相同,我们需要使用 plugin 对象的 unsubscribeEvent 方法取消注册的事件处理句柄。
this.plugin.unsubscribeEvent(EventType.WINDOW_ONDIDCHANGEACTIVETEXTEDITOR, eventHandler);
国际化
插件创建完后,在根目录下默认生成了 package.nls.json 和 package.nls.zh-cn.json 文件,package.nls.json 文件用来记录默认情况下的翻译词条,比如没有找到对应语言的翻译文件插件框架将默认采用该文件中的词条。package.nls.zh-cn.json 则是中文简体的翻译词条文件,如果插件需要支持其他语言也可以自行添加翻译文件。
localize 方法需要提供了一个 key 参数来指定使用国际化文件中的词条索引键值,后续的不定参数用来对翻译词条中的占位符进行替换,词条中支持使用"{0} {1} {2}"这样的格式进行占位,localize 方法的第二个参数开始会被依次替换到占位符中。
localize(key: string, ...args: any[]): string;
示例如下:
{ "plugin.welcome": "Welcome!", "plugin.hello": "Hello {0}!" }
- 内置成员plugin的localize方法
我们还在前后端内置的 plugin 成员变量中实现了 localize 方法。Frontend类 (src/browser/fronted.ts) 和 Backend类 (src/browser/backend.ts) 分别继承了 AbstractFrontend 前端类和 AbstractBackend 后端类,可以直接使用 this.plugin.localize 方法进行本地化翻译。
// 不带参数 this.plugin.localize('plugin.welcome'); // 带参数 this.plugin.localize('plugin.hello', 'world');
- 直接引入localize方法
import { localize } from '@cloudide/nls';
使用如下代码就可以将词条填充为: Hello World!
localize('plugin.hello', 'World');
- 页面文件中的国际化方法
通用插件可以使用 ejs 和 pug 引擎来渲染界面,无论是 ejs 还是 pug 引擎插件框架都为开发者提供了一个 l10n 内置对象,里面存储了当前所选语言的翻译词条列表。
对于选择 ejs 引擎来做界面渲染的开发者可以在 ejs 文件中使用如下方式来对需要本地化的文案进行翻译:
<%= l10n['plugin.hello'] %>
对于使用 pug 引擎的开发者可以使用如下方式:
#{l10n['plugin.hello']}