Open Pines-Cheng opened 4 years ago
求助,我照着这个方法写了一个theia自定义的插件api,
import { injectable } from 'inversify';
import { ExtPluginApiProvider, ExtPluginApi } from '@theia/plugin-ext/lib/common/plugin-ext-api-contribution';
import * as path from 'path';
@injectable()
export class MusicPluginApiProvider implements ExtPluginApiProvider {
provideApi(): ExtPluginApi {
return {
frontendExtApi: {
initPath: '../../lib/plugin/webworker/music-api-worker-provider.js',
initFunction: 'initializeApi',
initVariable: 'music_api_provider'
},
backendInitPath: path.join('@music/theia-plugin-ext/lib/plugin/node/music-api-node-provider.js')
};
}
}
插件api后端使用没有问题,可以正常加载,可以正常使用。 但是前端在插件引入的时候,会在控制台报这样的错: 看起来关键点在于我的webworker的文件没有找到,问题是这个路径,该怎么写,才能找到这个文件去执行呢?
总算是看到了,che-plugin还添加了一个backend的扩展,来处理前端对api-provider的请求,把文件返回给webworker。 感觉离实现距离还很远啊。。先给大哥点个赞!
有个问题,我基本上按照che-plugin的在写了
通过在MainPluginApiProvider
中通过rpc.set(xxx, new xxx)
来实现api.
但是在createAPIFactory
使用rpc.getProxy(xxx)
的时候,拿到该Api实现的对应的属性是undefined,这里不知道为什么。应该不需要我再在BackendApplication
中自己起一套rpc服务了吧,感觉都没问题,但是拿到的属性就是undefined,可以提供点排查这个问题的思路吗?
不好意思,,我找了下,找到了。。在plugin-ext/src/common/rpc-protocol
里面,设定了属性中$
开头的才会进行转发,
虽然原理我还是不太懂,不过目前整体的扩展插件api算是走通了,打扰了。
我有点混乱了,这几个究竟是什么关系啊? 打开看好像是 VSCode 的网页版。但是又是 eclipse 的?
我有点混乱了,这几个究竟是什么关系啊? 打开看好像是 VSCode 的网页版。但是又是 eclipse 的?
你可以看成是 Eclipse 官方重写了一个 VS Code,名字叫 Theia,很多能力、架构包括接口设计等都是和 VS Code 相通的。
Eclipse Theia 是一个可扩展的平台,可以利用最先进的 Web 技术开发多语言的 Cloud & Desktop IDE。
名词解释
概述
拓展 Theia Plugin 能力,让业务方简单、灵活地深度定制 IDE 的功能和界面。
动机
Theia Plugin 拓展方式和 能力 和 VSCode Extension 类似,并不满足我们的需求:
因此,需要拓展 Theia 的插件系统。
原则
设计概览
设计总结
整体设计图示
tide 项目结构
IDE Core 和 Taro IDE 暂时放在同一个项目 tide 里,建议参考:che-theia。
npm 包发布在
@tide
Scope 下。VS Code Extension(概念上等同于 Theia 的 Plugin)能力是以下三种方式拓展:
详细设计
项目将参考 VS Code Extension 的拓展接口及规范,从配置、Command 、VS Code API 等方面拓展插件系统,其中,VSCode 最具代表性的拓展例子应该是 Tree View API,兼具以上三者方式。
Contribution Points 配置拓展
Contribution Points
是package.json
中contributes
字段的一系列 JSON 声明,插件通过注册Contribution Points
来拓展 VSCode 的功能。contributes
配置的处理可以分为配置扫描(scanner)和配置处理(handler)。主要在 plugin-ext 里实现。scanner
plugin-ext/src/hosted/node/scanners/scanner-theia.ts 里的
TheiaPluginScanner
类实现了所有 package.json 配置的读取方法,包括 contribution 配置、activationEvents 配置等。我们应该是不需要添加新的配置读取,所以不需要修改这里。
handler
contribution 最终配置的 handle 都是在
PluginContributionHandler
里注入实际 handler 类统筹处理的。拓展
和 API 拓展不同专门预留了拓展注入点
ExtPluginApiProvider
不同,Theia 代码里类似的并没有预留专门的接口,暂时采用以下步骤拓展:TidePluginContributionHandler
继承PluginContributionHandler
类handleContributions
方法rebind(TidePluginContributionHandler).to(PluginContributionHandler).inSingletonScope();
如果有更好的方式,请指正。
Command 拓展
Commands 触发
Theia/VSCode
的 actions。VSCode 代码里包含大量 built-in commands,你可以使用这些命令与编辑器交互、控制用户界面或执行后台操作。Command 拓展可以参考:packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts
首先定义
XXXCommandsContribution
类实现CommandContribution
,并注入对应的服务,如然后在XXXCommandsContribution
中通过commands.registerCommand
进行 Command 拓展,如:然后 bind 到 container 即可:
XXXCommandsContribution 会被注入到对应的 ContributionProvider,然后进行处理:
Command 可以传入对象作为参数,无法暴露接口和组件。
API 拓展
相对上面两种拓展方式,API 的拓展方式比较复杂。
方式一:plugin-ext-vscode 的方式
这种方式是 VSCode 采用的方式,通过修改
PluginLifecycle
里面的backendInitPath
或frontendInitPath
,这两个脚本类似于 preload 脚本,在插件加载前进行预加载,初始化插件环境。具体是 VsCodePluginScanner 类里的
getLifecycle()
方法的backendInitPath
。在这里 backendInitPath 被初始化为:backendInitPath: __dirname + '/plugin-vscode-init.js'
然后在 PluginHostRPC 类里
new PluginManagerExtImpl()
实例时,在传入的 init 钩子中调用的initContext
中通过require()
方法加载。注意:
initContext
里面的backendInitPath
来自于PluginLifecycle
,并不是ExtPluginApiProvider
。而 backendInitPath 配置的
plugin-vscode-init.ts
文件提供了doInitialization
方法,在doInitialization
方法中通过Object.assign
合并 Theia API 到 vscode namespace,添加简单的 API 和字段。这种方法本质是在插件加载前运行脚本,不涉及到 RPC,通过
Object.assign
合并简单的 API。这种方式不如 ExtPluginApiProvider 的方式优雅,社区有人提里 PR 将其改成 ExtPluginApiProvider 的形式:Make "theia" and "vscode" contributed API's #8142,目前为止依然还没有被合并。
方式二:ExtPluginApiProvider 的方式
eclipse/che-theia 就是采用了这种方式,功能非常强大。具体可见:ChePluginApiProvider
Theia 官方文档没有提到这种方式,不过在 plugin-ext/doc 下倒是有一片简单的介绍文档:This document describes how to add new plugin api namespace
Che-Theia plug-in API 提供了 che 的 namespace。
主要步骤
主要步骤如下:
frontendExtApi
和后端入口backendInitPath
。createAPIFactory(rpc)
,然后分别挂载到前端及后端插件运行时。che-api-worker-provider.js
,实现并export initializeApi
方法。在 initializeApi 中,通过createAPIFactory(rpc)
定义接口。export provideApi()
,然后在overrideInternalLoad()
方法中改写module._load
,通过createAPIFactory(rpc)
定义接口。1. ExtPluginApiProvider 提供前后端入口
首先声明 ExtPluginApiProvider 实现:
然后注入到 backend moudule:
这样,前端及后台都有了插件拓展的入口。
2. Client API 的接口
createAPIFactory(rpc)
createAPIFactory 用于定义 Client API 接口,然后分别挂载到前端及后端插件运行时的 namespace。
createAPIFactory 方法的实现,和 Theia 源码中
packages/plugin-ext/src/plugin/plugin-context.ts
里 createAPIFactory 的实现一致:前后端 Client API 注入
new PluginManagerExtImpl()
传入的第一个参数 host 是 PluginHost 类型,其中的 initExtApi 等方法前端后台分别实现:initExtApi 前端,挂在 window 下。
其中
const pluginsModulesNames = new Map<string, Plugin>();
插件的集合。initExtApi 后端,直接 require,并运行 provideApi()
3. 前端入口 initializeApi,挂载 API 到 namespace
前端入口脚本
che-api-worker-provider.js
,实现并export initializeApi
方法。在 initializeApi 中,传入 RPC,挂载到 che namespace。4. 后端入口 provideApi,load 时代理 API
后端入口脚本
che-api-node-provider.js
,代码里需要暴露export provideApi()
。后端也是通过 createAPIFactory 定义 Client API 接口。
然后在
overrideInternalLoad()
方法中改写module._load
,使require('@eclipse-che/plugin')
返回定义的 Client API。5. Server API 的注入
MainPluginApiProvider 的实现应该包含新命名空间的 Plugin API 的 main(接口实现)部分。
注入到浏览器的 HostedPluginSupport 中,然后在 initRpc方法一次调用注入 MainPluginApiProvider 的 initialize 方法进行初始化。
简化的 API 通信架构图大致如下:
ExtPluginApiProvider 的拓展方式非常成熟优雅且功能强大,建议采用这一种。
Demo
见 Tide 项目 master 分支 extension/tide-theia-plugin-ext 模块。
参考