
IDE代码编辑器开发工具前端桌面应用插件系统后端AI 应用【免费下载链接】theiaEclipse Theia is a cloud desktop IDE framework implemented in TypeScript.项目地址https://gitcode.com/gh_mirrors/th/theia点击查看免费下载theia/api-provider-sample位于 examples/api-provider-sample是 Eclipse Theia 官方仓库中的一个编程示例扩展用于演示如何为插件plugin定义并提供自定义的 API 对象。它实现了一个名为gotdGreeting of the Day今日问候语的插件 API插件可以在激活函数中导入该 API创建问候器Greeter并获取风格各异的问候消息。阅读本文后你将掌握 Theia API Provider 扩展的完整骨架——从gotd.d.ts类型声明、RPC 接口定义到主进程侧实现与插件进程侧实现的 RPC 打通以及如何通过 Inversify 依赖注入完成注册并最终写一个真正消费该 API 的插件。一、扩展概览它解决什么问题Theia 的插件体系plugin system让插件运行在独立的plugin-host Node 进程中与主 Theia 进程隔离相关架构说明见 doc/Plugin-API.md。插件进程与主进程之间通过RPC远程过程调用通信而vscode这样的 API 对象正是由theia/plugin-ext以API Provider的形式注入给插件的。theia/api-provider-sample的核心目标是见 README为开发者提供定义自定义 API 对象的真实可运行的编码范例为审查 Pull Request 时提供易用、易测的功能示例。该扩展仅作参考与测试用途package.json中声明了private: true因此不会发布到 npm见 package.json。扩展名为theia/api-provider-sample版本与仓库一致当前为1.75.0并声明了theiaExtensions的backend入口指向编译后的lib/node/gotd-backend-module{ private: true, name: theia/api-provider-sample, version: 1.75.0, description: Theia - Example code to demonstrate Theia API Provider Extensions, dependencies: { theia/core: 1.75.0, theia/plugin-ext: 1.75.0, theia/plugin-ext-headless: 1.75.0 }, theiaExtensions: [ { backend: lib/node/gotd-backend-module } ], types: src/gotd.d.ts }依赖的三个包各有分工theia/core提供事件Event/Emitter、Disposable等基础设施theia/plugin-ext提供 RPC 协议RPCProtocol、createProxyIdentifier与MainPluginApiProvider等主进程侧接口theia/plugin-ext-headless提供ExtPluginApiProvider接口——它使得同一个自定义 API 既能被普通后端插件消费也能被 headless 插件消费。这正是本示例同时依赖后两者的原因。二、源码布局三层目录结构扩展的源码全部位于src/目录下共四个部分见 READMEexamples/api-provider-sample/src/ ├── gotd.d.ts # gotd API 的 TypeScript 类型声明插件侧导入 ├── common/ │ └── plugin-api-rpc.ts # RPC 的 Ext/Main 两侧接口定义 ├── node/ │ ├── gotd-backend-module.ts # Inversify 容器模块注册 API Provider │ ├── ext-plugin-gotd-api-provider.ts # ExtPluginApiProvider 实现 │ ├── gotd-main-plugin-provider.ts # MainPluginApiProvider 实现 │ └── greeting-main-impl.ts # GreetingMain 接口的主进程实现 └── plugin/ ├── gotd-api-init.ts # 插件 API 初始化脚本 └── greeting-ext-impl.ts # GreetingExt 接口的插件进程实现各目录的运行环境与职责gotd.d.ts—gotdAPI 对象的 TypeScript 定义插件通过导入它来与今日问候语服务交互。package.json的types: src/gotd.d.ts让插件可以直接import * as gotd from theia/api-provider-sample获得类型提示。plugin/— API 初始化脚本与 API 对象的实现GreetingExt等接口的实现类。该目录下所有代码只运行在独立的 plugin-host Node 进程中与主 Theia 进程隔离它服务的对象既包括 headless 插件也包括 VS Code 插件的后端部分。GreetingExtImpl等类通过RPC与主进程中实际的 API 实现GreetingMainImpl等通信。node/— 实现GreetingMain等接口的 API 类以及注册 API Provider 的 Inversify 绑定。该目录下所有代码运行在主 Theia Node 进程中。common/— 与gotd插件 API 后端对应的RPC Ext/Main 接口定义同时被两侧进程引用。三、插件侧 API 类型定义gotd.d.tsgotd.d.ts是插件开发者唯一需要接触的门面完整源码export namespace greeting { export function createGreeter(): Promisegreeting.Greeter; export enum GreetingKind { DIRECT 1, QUIRKY 2, SNARKY 3, } export interface Greeter extends Disposable { greetingKinds: readonly GreetingKind[]; getMessage(): Promisestring; setGreetingKind(kind: GreetingKind, enable true): void; onGreetingKindsChanged: Eventreadonly GreetingKind[]; } } export interface EventT { (listener: (e: T) unknown, thisArg?: unknown): Disposable; } export interface Disposable { dispose(): void; }需要理解的关键点greeting命名空间源码注释明确说明严格来说greeting命名空间是一个不必要的组织层级但它正好用来演示后端是如何实现 API 命名空间的。即API Provider 可以在顶层导出对象如Disposable也可以提供嵌套命名空间如greeting插件进程侧会按相同结构组装返回。GreetingKind枚举定义三种问候风格——DIRECT直白、QUIRKY古怪、SNARKY尖刻。注意它在gotd.d.ts与common/plugin-api-rpc.ts中各声明了一份因为两侧进程各自需要这个枚举插件侧用于构造 API 对象主进程侧用于解释 RPC 消息中的数值。Greeter接口一个问候器继承Disposable暴露只读的greetingKinds列表、getMessage()获取随机问候语、setGreetingKind()启停某种问候风格第二参数enable默认为true以及onGreetingKindsChanged事件用于监听风格变化。EventT的签名与 Theia 核心的事件模型一致订阅返回Disposable。四、RPC 接口定义common/plugin-api-rpc.tscommon/plugin-api-rpc.ts完整源码是两侧进程共享的 RPC 协议契约定义了谁调谁的接口与方法名规范。主进程侧接口GreetingMain带$前缀的方法表示由插件进程 RPC 调用主进程export const GreetingMain Symbol(GreetingMain); export interface GreetingMain { $getMessage(greeterId: string): Promisestring; $createGreeter(): PromiseGreeterData; $destroyGreeter(greeterId: GreeterData[uuid]): Promisevoid; $updateGreeter(data: GreeterData): void; }插件进程侧接口GreetingExt分两部分源码export const GreetingExt Symbol(GreetingExt); export interface GreetingExt { // External protocol供 API 工厂实现调用的对外方法 registerGreeter(): Promisestring; unregisterGreeter(uuid: string): Promisevoid; getMessage(greeterId: string): Promisestring; getGreetingKinds(greeterId: string): readonly greeting.GreetingKind[]; setGreetingKindEnabled(greeterId: string, greetingKind: greeting.GreetingKind, enable: boolean): void; onGreetingKindsChanged(greeterId: string): Eventreadonly greeting.GreetingKind[]; // Internal protocol供主进程 RPC 回调插件进程 $greeterUpdated(data: GreeterData): void; }External protocol供 API 工厂实现GotdApiFactoryImpl在本地调用Internal protocol$greeterUpdated带$前缀表示它是由主进程 RPC 反向推送事件到插件进程的方法。通信端点通过createProxyIdentifier来自theia/plugin-ext/lib/common/rpc-protocol注册export const PLUGIN_RPC_CONTEXT { GREETING_MAIN: createProxyIdentifierGreetingMain(GreetingMain), }; export const MAIN_RPC_CONTEXT { GREETING_EXT: createProxyIdentifierGreetingExt(GreetingExt), };此外还定义了跨进程传输的数据结构GreeterDatauuidgreetingKinds数组它是Greeter状态的序列化载体主进程持有权威状态插件进程持有其副本。五、主进程侧API 实现与注册node/5.1 后端容器模块一切的入口gotd-backend-module.ts 是package.json中theiaExtensions.backend指向的模块负责把三类绑定注册进主进程的 Inversify 容器import { ContainerModule } from theia/core/shared/inversify; import { ExtPluginApiProvider } from theia/plugin-ext; import { ExtPluginGotdApiProvider } from ./ext-plugin-gotd-api-provider; import { MainPluginApiProvider } from theia/plugin-ext/lib/common/plugin-ext-api-contribution; import { GotdMainPluginApiProvider } from ./gotd-main-plugin-provider; import { GreetingMain } from ../common/plugin-api-rpc; import { GreetingMainImpl } from ./greeting-main-impl; export default new ContainerModule(bind { bind(Symbol.for(ExtPluginApiProvider)).to(ExtPluginGotdApiProvider).inSingletonScope(); bind(MainPluginApiProvider).to(GotdMainPluginApiProvider).inSingletonScope(); bind(GreetingMain).to(GreetingMainImpl).inSingletonScope(); });三个绑定的含义ExtPluginApiProvider来自theia/plugin-ext告诉插件系统本扩展提供了一个 API 注入点并指出插件进程侧的初始化脚本路径MainPluginApiProvider来自theia/plugin-ext/lib/common/plugin-ext-api-contribution当 RPC 通道就绪时把主进程实现对象注册到 RPC 上供插件进程代理调用GreetingMain将接口符号绑定到具体实现GreetingMainImpl单例。5.2 ExtPluginApiProvider声明初始化脚本ext-plugin-gotd-api-provider.ts 实现了ExtPluginApiProvider.provideApi()返回 API 的初始化入口路径import * as path from path; import { injectable } from theia/core/shared/inversify; import { ExtPluginApi, ExtPluginApiProvider } from theia/plugin-ext-headless; injectable() export class ExtPluginGotdApiProvider implements ExtPluginApiProvider { provideApi(): ExtPluginApi { // 同时支持后端插件与 headless 插件因此只需一个入口脚本 // 应用构建会把该脚本从源码 ../plugin/ 打包到 ../backend/ 目录 // 与其它所有插件 API Provider 的脚本放在一起。 const universalInitPath path.join(__dirname, ../backend/gotd-api-init); return { backendInitPath: universalInitPath, headlessInitPath: universalInitPath }; } }要点backendInitPath与headlessInitPath指向同一个初始化脚本这是示例特意展示的通用入口用法说明同一份 API 初始化代码可以同时服务普通后端插件和 headless 插件。5.3 MainPluginApiProvider把实现挂到 RPC 上gotd-main-plugin-provider.ts 在 RPC 初始化时调用rpc.set(...)把GreetingMainImpl单例注册为插件进程可代理调用的远端对象injectable() export class GotdMainPluginApiProvider implements MainPluginApiProvider { inject(GreetingMain) protected readonly greetingMain: GreetingMain; initialize(rpc: RPCProtocol): void { rpc.set(PLUGIN_RPC_CONTEXT.GREETING_MAIN, this.greetingMain); } }5.4 GreetingMainImpl主进程的真正实现greeting-main-impl.ts 持有问候语的权威数据GREETINGS常量表按三种GreetingKind各定义了三句问候语构造函数通过rpc.getProxy(MAIN_RPC_CONTEXT.GREETING_EXT)拿到插件进程侧GreetingExt的代理用于反向通知。const GREETINGS { [GreetingKind.DIRECT]: [Hello, world!, Im here!, Good day!], [GreetingKind.QUIRKY]: [Howdy doody, world?, Whats crack-a-lackin?, Wazzup werld?], [GreetingKind.SNARKY]: [Oh, its you, world., You again, world?!, Whatever.], } as const;核心逻辑源码$createGreeter()用generateUuid()来自theia/core/lib/common/uuid生成唯一 ID默认开启DIRECT风格存入greeterData后返回$destroyGreeter()按greeterId删除记录$updateGreeter()更新主进程侧状态后调用this.proxy.$greeterUpdated({ ...myData })把变化推回插件进程从而触发插件侧的onGreetingKindsChanged事件$getMessage()从当前启用的风格中随机选一种再从该风格的问候语表中随机选一句返回如果没有任何启用的风格则抛出No greetings are available for greeter ${greeterId}错误。六、插件进程侧API 工厂与实现plugin/6.1 初始化脚本 gotd-api-init.tsgotd-api-init.ts 是插件进程侧的核心它负责在插件模块导入自定义 API 时创建并返回该 API 对象。它导出一个containerModuleTheia 会用它来配置插件进程的 Inversify 容器并在 plugin-host 进程 fork 时调用export const containerModule PluginContainerModule.create(({ bind, bindApiFactory }) { bind(GreetingExt).to(GreetingExtImpl).inSingletonScope(); bindApiFactory(theia/api-provider-sample, GotdApiFactory, GotdApiFactoryImpl); });PluginContainerModule.create与bindApiFactory来自theia/plugin-ext/lib/plugin/node/plugin-container-module。bindApiFactory(theia/api-provider-sample, ...)表示当某个插件require(theia/api-provider-sample)时插件系统会调用GotdApiFactoryImpl.createApi(plugin)来构造 API 对象。工厂实现类源码injectable() class GotdApiFactoryImpl { inject(RPCProtocol) protected readonly rpc: RPCProtocol; inject(GreetingExt) protected readonly greetingExt: GreetingExt; postConstruct() initialize(): void { this.rpc.set(MAIN_RPC_CONTEXT.GREETING_EXT, this.greetingExt); } createApi(plugin: Plugin): Gotd { const self this; async function createGreeter(): Promisegotd.greeting.Greeter { const toDispose new DisposableCollection(); const uuid await self.greetingExt.registerGreeter(); toDispose.push(Disposable.create(() self.greetingExt.unregisterGreeter(uuid))); const onGreetingKindsChanged self.greetingExt.onGreetingKindsChanged(uuid); const result: gotd.greeting.Greeter { get greetingKinds(): readonly GreetingKind[] { return self.greetingExt.getGreetingKinds(uuid); }, setGreetingKind(greetingKind: GreetingKind, enable true): void { self.greetingExt.setGreetingKindEnabled(uuid, greetingKind, enable); }, getMessage(): Promisestring { return self.greetingExt.getMessage(uuid); }, onGreetingKindsChanged, dispose: toDispose.dispose.bind(toDispose), }; return result; } const greeting: Gotd[greeting] { createGreeter, GreetingKind }; return { greeting, Disposable, }; }; }值得注意的设计细节postConstruct initialize()在工厂初始化时把GreetingExtImpl注册到 RPC 上rpc.set(MAIN_RPC_CONTEXT.GREETING_EXT, ...)这样主进程的GreetingMainImpl才能反向调用插件进程createApi(plugin: Plugin)接收当前插件对象作为参数返回类型为Gotd即typeof gotd结构与gotd.d.ts中的命名空间一一对应顶层导出Disposable与greeting命名空间greeting内导出createGreeter与GreetingKindcreateGreeter()的每个Greeter都通过DisposableCollection管理生命周期dispose()时会自动调用unregisterGreeter(uuid)确保插件停用时主进程侧的记录也被清理greetingKinds用 getter 实时从GreetingExt读取保证拿到的是最新状态。6.2 GreetingExtImpl插件侧的 RPC 客户端greeting-ext-impl.ts 实现GreetingExt接口。构造函数通过rpc.getProxy(PLUGIN_RPC_CONTEXT.GREETING_MAIN)获得主进程GreetingMain的代理此后本地调用即被序列化为 RPC 消息发往主进程。各方法行为源码registerGreeter()调用主进程$createGreeter()拿到GreeterData在本进程greeterData表中登记并为其创建一个Emitter用于onGreetingKindsChanged事件最后返回uuidunregisterGreeter()删除本地记录并调用主进程$destroyGreeter(uuid)setGreetingKindEnabled()先在本地判断是否已经处于目标状态data.greetingKinds.includes(greetingKind) enable若无需变化则直接返回否则增删元素并通过$updateGreeter()同步给主进程getMessage()直接转发给主进程$getMessage()$greeterUpdated()被主进程 RPC 回调更新本地greetingKinds副本并fire事件从而通知插件侧所有订阅者。这个类揭示了事件的双向传播路径插件本地修改 →$updateGreeter→ 主进程更新并$greeterUpdated回推 → 插件进程Emitter.fire→ 插件订阅回调执行。七、调用链全景一次完整的问候流程综合以上代码可以梳理出gotdAPI 从创建到使用的完整调用链从源码结构推断Theia 启动加载theia/api-provider-sample扩展执行 gotd-backend-module.ts注册ExtPluginGotdApiProvider、GotdMainPluginApiProvider与GreetingMainImplplugin-host 进程 fork 时加载 gotd-api-init.ts 的containerModule绑定GreetingExtImpl与GotdApiFactoryImpl并在postConstruct中把GreetingExtImpl注册到 RPC主进程 RPC 通道建立后GotdMainPluginApiProvider.initialize(rpc)把GreetingMainImpl注册为远端对象两侧代理就绪插件激活时执行gotd.greeting.createGreeter()插件进程GreetingExtImpl.registerGreeter()→ RPC → 主进程$createGreeter()生成uuid并返回工厂用返回的uuid组装出Greeter对象交给插件插件调用greeter.getMessage()插件进程转发 → 主进程$getMessage()随机选取问候语 → 结果经 RPC 返回插件进程并 resolve插件调用greeter.setGreetingKind(QUIRKY)本地更新 →$updateGreeter→ 主进程更新状态并$greeterUpdated回推 → 插件进程fire事件 → 插件订阅回调收到onGreetingKindsChanged。八、配套示例插件plugin-gotd仓库中提供了真实的消费端示例 sample-plugins/sample-namespace/plugin-gotd。该插件的package.jsonpackage.json把theia/api-provider-sample作为devDependencies引入并声明了theiaPlugin.headless指向headless入口也就是说它同时具有 VS Code 插件后端与 headless 两种激活路径{ name: plugin-gotd, engines: { vscode: ^1.125.0 }, main: extension, activationEvents: [*], devDependencies: { theia/api-provider-sample: 1.75.0, types/vscode: ^1.125.0 }, theiaPlugin: { headless: headless }, headless: { activationEvents: [*], contributes: {} } }headless 入口 headless.js 展示了插件侧完整用法const gotd require(theia/api-provider-sample); const GreetingKind gotd.greeting.GreetingKind; async function greet(greeter) { const message await greeter.getMessage(); console.log([GOTD], message); } async function activate() { const greeter await gotd.greeting.createGreeter(); greeter.onGreetingKindsChanged(kinds { console.log([GOTD], Now supporting these kinds of greeting: ${greetingKindsToString(kinds)}.); if (kinds.length 0) { greet(greeter); } }); greet(greeter); later(() greeter.setGreetingKind(GreetingKind.DIRECT, false)); later(() greeter.setGreetingKind(GreetingKind.QUIRKY)); later(() greeter.setGreetingKind(GreetingKind.SNARKY)); } module.exports { activate, deactivate: /* 逐个 dispose 所有 greeter */ };这段代码演示了本 API 的全部能力createGreeter()创建问候器 →getMessage()立即取一条DIRECT问候语 → 随后依次关闭DIRECT、开启QUIRKY、再开启SNARKY每次变更都会通过onGreetingKindsChanged事件触发新的问候输出deactivate时通过Disposable.dispose()统一清理保证插件卸载后主进程状态同步清除。另一个入口 extension.js 则是标准的 VS Code 后端插件在activate中打印运行环境信息验证自定义 API 与 VS Code API 可以共存于同一插件。九、如何构建、运行与验证由于该扩展是 Theia 仓库的 monorepo 成员构建与运行都遵循 Theia 的标准流程参考 doc/Developing.md安装依赖在仓库根目录执行yarnTheia 使用 yarn workspaces 管理多包仓库根目录 package.json 定义了各工作区与脚本构建扩展在examples/api-provider-sample目录下执行yarn build该命令会调用theiaext buildtheia/ext-scripts把src/下的 TypeScript 编译到lib/。注意构建产物路径与源码路径不同插件初始化脚本从src/plugin/被打包到lib/backend/这正是 ext-plugin-gotd-api-provider.ts 里path.join(__dirname, ../backend/gotd-api-init)的由来运行该示例作为 Theia 扩展被examples/browser、examples/electron等应用工程通过theiaExtensions机制加载同时配合plugin-gotd示例插件即可在应用启动后于控制台观察到[GOTD]前缀的问候日志见 sample-plugins/sample-namespace/plugin-gotd作为模板复用如果你要为自己的产品定义自定义 API只需照此模式替换四样东西——gotd.d.ts中的 API 类型、common/plugin-api-rpc.ts中的 RPC 接口、node/下的主进程实现与绑定、plugin/下的工厂与插件进程实现并在package.json的theiaExtensions.backend指向你的后端模块即可。十、小结与扩展阅读theia/api-provider-sample用不到十个文件、一个命名空间级别的 API完整演示了 Theia 自定义插件 API 的全部机制类型声明gotd.d.ts→ RPC 契约common/→ 主进程实现与注册node/→ 插件进程工厂与实现plugin/→ 消费端示例plugin-gotd。其中ExtPluginApiProvider来自theia/plugin-ext-headless与MainPluginApiProvider来自theia/plugin-ext分别打通了插件进程如何找到 API 初始化脚本与主进程如何暴露实现两个方向RPCProtocol/createProxyIdentifier则负责把接口符号映射为可跨进程调用的代理。进一步深入可参考仓库内相关资源doc/Plugin-API.mdTheia 插件 API 总体架构说明packages/plugin-ext 与 packages/plugin-ext-headlessRPCProtocol、ExtPluginApiProvider、PluginContainerModule等机制的实际实现examples/api-provider-sample/src/gotd.d.ts 与 examples/api-provider-sample/src/common/plugin-api-rpc.ts本示例的类型与 RPC 契约源码sample-plugins/sample-namespace/plugin-gotd可直接运行消费该 API 的示例插件。该扩展遵循 Eclipse Public License 2.0 与带 Classpath Exception 的 GPL-2.0 双许可见 READMETheia 是 Eclipse Foundation 的商标。赞分享IDE代码编辑器开发工具前端桌面应用插件系统后端AI 应用【免费下载链接】theiaEclipse Theia is a cloud desktop IDE framework implemented in TypeScript.项目地址https://gitcode.com/gh_mirrors/th/theia点击查看免费下载相关推荐Eclipse Theia 示例插件Sample Plugins完全指南编写、打包与在 Theia 中验证 VS Code 扩展Eclipse Theia 示例插件Sample Plugins完全指南编写、打包与在 Theia 中验证 VS Code 扩展 sample plugiIDE代码编辑器开发工具前端桌面应用插件系统后端AI 应用中文医疗大模型全景实战指南基座选型、医学数据构建与微调算力解析Awesome-Chinese-LLM 医学篇中文医疗大模型全景实战指南基座选型、医学数据构建与微调算力解析Awesome Chinese LLM 医学篇 本文以开源仓库 Awesome ChinesIDE代码编辑器开发工具前端桌面应用插件系统后端AI 应用Eclipse Theia API Samples 扩展指南用内部 API 与依赖注入编写真实可测的示例代码Eclipse Theia API Samples 扩展指南用内部 API 与依赖注入编写真实可测的示例代码 theia/api samples 是 EclIDE代码编辑器开发工具前端桌面应用插件系统后端AI 应用上一篇开源项目推荐TlbbGmTool——游戏开发者的得力助手下一篇PgQueuer基于PostgreSQL的高效任务队列库创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考